gavaengine 0.1.0 → 1.2.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.
@@ -1,8 +1,320 @@
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
- name: "GavaEngine",
5
- splashWords: ["Powerful", "Flexible", "Modular", "Secure", "Customizable"]
317
+ name: "GavaEngine"
6
318
  },
7
319
  roles: {
8
320
  list: ["admin", "redattore", "scrittore", "lettore"],
@@ -16,6 +328,18 @@ var DEFAULT_CONFIG = {
16
328
  canPublish: (role) => role === "admin" || role === "redattore",
17
329
  adminRole: "admin"
18
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
+ },
19
343
  categories: [
20
344
  "Cultura ed Intercultura",
21
345
  "Fatti ed Eventi",
@@ -50,95 +374,7 @@ var DEFAULT_CONFIG = {
50
374
  login: "/login",
51
375
  home: "/"
52
376
  },
53
- strings: {
54
- articles: "Articoli",
55
- newArticle: "Nuovo articolo",
56
- media: "Media",
57
- users: "Utenti",
58
- statistics: "Statistiche",
59
- settings: "Impostazioni",
60
- revisions: "Cronologia",
61
- publish: "Pubblica",
62
- unpublish: "Ritira",
63
- saveDraft: "Salva bozza",
64
- delete: "Elimina",
65
- edit: "Modifica",
66
- search: "Cerca",
67
- titlePlaceholder: "Titolo dell'articolo",
68
- excerptPlaceholder: "Breve descrizione dell'articolo...",
69
- slugPlaceholder: "slug-articolo",
70
- authorPlaceholder: "Nome dell'autore",
71
- category: "Categoria",
72
- selectCategory: "Seleziona categoria",
73
- excerpt: "Estratto",
74
- author: "Autore",
75
- slugUrl: "Slug URL",
76
- draft: "Bozza",
77
- published: "Pubblicato",
78
- all: "Tutti",
79
- drafts: "Bozze",
80
- saving: "Salvando...",
81
- saved: "Salvato",
82
- saveError: "Errore nel salvataggio",
83
- noArticles: "Nessun articolo.",
84
- noArticlesSearch: "Nessun articolo trovato.",
85
- noMedia: "Nessun file caricato.",
86
- noMediaSearch: "Nessun file trovato.",
87
- noRevisions: "Nessuna revisione salvata.",
88
- loading: "Caricamento...",
89
- confirm: "Conferma",
90
- cancel: "Annulla",
91
- apply: "Applica",
92
- change: "Cambia",
93
- logout: "Esci",
94
- viewArticle: "Vedi articolo pubblicato",
95
- preview: "Anteprima",
96
- restore: "Ripristina",
97
- restoreConfirm: "Ripristinare questa revisione? Lo stato attuale verr\xE0 salvato prima del ripristino.",
98
- beforeRestore: "Prima del ripristino",
99
- publishedNote: "Pubblicato",
100
- deleteConfirm: (title) => `Eliminare "${title || "Senza titolo"}"?`,
101
- unpublishConfirm: (title) => `Ritirare "${title || "Senza titolo"}"? L'articolo torner\xE0 in bozza.`,
102
- deleteUserConfirm: (name) => `Sei sicuro di voler eliminare l'utente "${name}"?`,
103
- totalArticles: (count) => `${count} articol${count === 1 ? "o" : "i"} totali`,
104
- totalFiles: (count) => `${count} file caricati`,
105
- coverImageLabel: "Immagine di copertina",
106
- coverImageHint: "Scegli dalla libreria o carica un nuovo file",
107
- chooseFile: "Scegli file",
108
- dragHint: "Trascina un'immagine qui oppure",
109
- uploadHint: "JPEG, PNG, WebP, GIF (max 5MB)",
110
- mediaLibrary: "Libreria media",
111
- uploadNew: "Carica nuovo",
112
- searchFiles: "Cerca file...",
113
- searchArticles: "Cerca articoli...",
114
- selectImage: "Seleziona immagine",
115
- noImages: "Nessuna immagine nella libreria.",
116
- noImagesSearch: "Nessuna immagine trovata.",
117
- copyUrl: "Copia URL",
118
- editImage: "Modifica immagine",
119
- restoreOriginal: "Ripristina originale",
120
- freeAspect: "Libero",
121
- unauthorized: "Non autorizzato",
122
- notAuthenticated: "Non autenticato",
123
- allFieldsRequired: "Tutti i campi sono obbligatori.",
124
- passwordMinLength: "La password deve avere almeno 6 caratteri.",
125
- emailExists: "Esiste gi\xE0 un utente con questa email.",
126
- invalidRole: "Ruolo non valido.",
127
- cannotDeleteSelf: "Non puoi eliminare il tuo stesso account.",
128
- name: "Nome",
129
- email: "Email",
130
- password: "Password",
131
- passwordEditHint: " (lascia vuoto per non modificarla)",
132
- role: "Ruolo",
133
- createdAt: "Creato il",
134
- actions: "Azioni",
135
- createUser: "Crea utente",
136
- saveChanges: "Salva modifiche",
137
- updated: "Aggiornato",
138
- status: "Stato",
139
- title: "Titolo",
140
- untitled: "Senza titolo"
141
- }
377
+ strings: DEFAULT_STRINGS
142
378
  };
143
379
  function deepMerge(target, source) {
144
380
  const result = { ...target };
@@ -157,32 +393,47 @@ function deepMerge(target, source) {
157
393
  return result;
158
394
  }
159
395
  function defineConfig(overrides = {}) {
160
- 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);
161
410
  }
162
411
 
163
412
  // src/handlers/revisions.ts
164
413
  function createRevisionHandlers(prisma, config) {
165
414
  const throttleMs = config.editor.revisionThrottleMinutes * 60 * 1e3;
415
+ const articleModel = config.models.article;
416
+ const revisionModel = config.models.articleRevision;
166
417
  return {
167
418
  async getRevisions(articleId) {
168
- return prisma.articleRevision.findMany({
419
+ return prisma[revisionModel].findMany({
169
420
  where: { articleId },
170
421
  include: { editor: { select: { name: true } } },
171
422
  orderBy: { createdAt: "desc" }
172
423
  });
173
424
  },
174
425
  async restoreRevision(articleId, revisionId, editorId) {
175
- const article = await prisma.article.findUnique({
426
+ const article = await prisma[articleModel].findUnique({
176
427
  where: { id: articleId }
177
428
  });
178
429
  if (!article) throw new Error("Article not found");
179
- const revision = await prisma.articleRevision.findUnique({
430
+ const revision = await prisma[revisionModel].findUnique({
180
431
  where: { id: revisionId }
181
432
  });
182
433
  if (!revision || revision.articleId !== articleId) {
183
434
  throw new Error("Revision not found");
184
435
  }
185
- await prisma.articleRevision.create({
436
+ await prisma[revisionModel].create({
186
437
  data: {
187
438
  articleId,
188
439
  title: article.title,
@@ -194,7 +445,7 @@ function createRevisionHandlers(prisma, config) {
194
445
  note: config.strings.beforeRestore
195
446
  }
196
447
  });
197
- await prisma.article.update({
448
+ await prisma[articleModel].update({
198
449
  where: { id: articleId },
199
450
  data: {
200
451
  title: revision.title,
@@ -208,17 +459,17 @@ function createRevisionHandlers(prisma, config) {
208
459
  return { success: true };
209
460
  },
210
461
  async createRevisionSnapshot(articleId, editorId, note = "") {
211
- const article = await prisma.article.findUnique({
462
+ const article = await prisma[articleModel].findUnique({
212
463
  where: { id: articleId }
213
464
  });
214
465
  if (!article) return;
215
466
  const threshold = new Date(Date.now() - throttleMs);
216
- const recent = await prisma.articleRevision.findFirst({
467
+ const recent = await prisma[revisionModel].findFirst({
217
468
  where: { articleId, createdAt: { gte: threshold } },
218
469
  orderBy: { createdAt: "desc" }
219
470
  });
220
471
  if (recent && !note) return;
221
- await prisma.articleRevision.create({
472
+ await prisma[revisionModel].create({
222
473
  data: {
223
474
  articleId,
224
475
  title: article.title,
@@ -237,19 +488,20 @@ function createRevisionHandlers(prisma, config) {
237
488
  // src/handlers/articles.ts
238
489
  function createArticleHandlers(prisma, config) {
239
490
  const revisionHandlers = createRevisionHandlers(prisma, config);
491
+ const articleModel = config.models.article;
240
492
  return {
241
493
  async getArticles() {
242
- return prisma.article.findMany({
494
+ return prisma[articleModel].findMany({
243
495
  orderBy: { updatedAt: "desc" }
244
496
  });
245
497
  },
246
498
  async getArticleById(id) {
247
- return prisma.article.findUnique({
499
+ return prisma[articleModel].findUnique({
248
500
  where: { id }
249
501
  });
250
502
  },
251
503
  async createArticle(authorName) {
252
- const article = await prisma.article.create({
504
+ const article = await prisma[articleModel].create({
253
505
  data: {
254
506
  authorName
255
507
  }
@@ -257,23 +509,23 @@ function createArticleHandlers(prisma, config) {
257
509
  return article.id;
258
510
  },
259
511
  async updateArticle(userId, id, data) {
260
- const article = await prisma.article.findUnique({ where: { id } });
512
+ const article = await prisma[articleModel].findUnique({ where: { id } });
261
513
  if (!article) throw new Error("Article not found");
262
514
  await revisionHandlers.createRevisionSnapshot(id, userId);
263
- await prisma.article.update({
515
+ await prisma[articleModel].update({
264
516
  where: { id },
265
517
  data: { ...data, updatedAt: /* @__PURE__ */ new Date() }
266
518
  });
267
519
  return { success: true };
268
520
  },
269
521
  async deleteArticle(id) {
270
- const article = await prisma.article.findUnique({ where: { id } });
522
+ const article = await prisma[articleModel].findUnique({ where: { id } });
271
523
  if (!article) throw new Error("Article not found");
272
- await prisma.article.delete({ where: { id } });
524
+ await prisma[articleModel].delete({ where: { id } });
273
525
  return { success: true };
274
526
  },
275
527
  async publishArticle(userId, id) {
276
- const article = await prisma.article.findUnique({ where: { id } });
528
+ const article = await prisma[articleModel].findUnique({ where: { id } });
277
529
  if (!article) throw new Error("Article not found");
278
530
  if (!article.title || !article.slug) {
279
531
  throw new Error(
@@ -285,22 +537,22 @@ function createArticleHandlers(prisma, config) {
285
537
  userId,
286
538
  config.strings.publishedNote
287
539
  );
288
- await prisma.article.update({
540
+ await prisma[articleModel].update({
289
541
  where: { id },
290
542
  data: {
291
- status: "pubblicato",
543
+ status: config.statuses.published,
292
544
  publishedAt: /* @__PURE__ */ new Date()
293
545
  }
294
546
  });
295
547
  return { success: true };
296
548
  },
297
549
  async unpublishArticle(id) {
298
- const article = await prisma.article.findUnique({ where: { id } });
550
+ const article = await prisma[articleModel].findUnique({ where: { id } });
299
551
  if (!article) throw new Error("Article not found");
300
- await prisma.article.update({
552
+ await prisma[articleModel].update({
301
553
  where: { id },
302
554
  data: {
303
- status: "bozza",
555
+ status: config.statuses.draft,
304
556
  publishedAt: null
305
557
  }
306
558
  });
@@ -312,9 +564,10 @@ function createArticleHandlers(prisma, config) {
312
564
  // src/handlers/users.ts
313
565
  import bcrypt from "bcryptjs";
314
566
  function createUserHandlers(prisma, config) {
567
+ const userModel = config.models.user;
315
568
  return {
316
569
  async getUsers() {
317
- return prisma.user.findMany({
570
+ return prisma[userModel].findMany({
318
571
  select: {
319
572
  id: true,
320
573
  name: true,
@@ -326,7 +579,7 @@ function createUserHandlers(prisma, config) {
326
579
  });
327
580
  },
328
581
  async getUserById(id) {
329
- return prisma.user.findUnique({
582
+ return prisma[userModel].findUnique({
330
583
  where: { id },
331
584
  select: {
332
585
  id: true,
@@ -350,12 +603,12 @@ function createUserHandlers(prisma, config) {
350
603
  if (password.length < 6) {
351
604
  return { error: config.strings.passwordMinLength };
352
605
  }
353
- const existing = await prisma.user.findUnique({ where: { email } });
606
+ const existing = await prisma[userModel].findUnique({ where: { email } });
354
607
  if (existing) {
355
608
  return { error: config.strings.emailExists };
356
609
  }
357
610
  const hashedPassword = await bcrypt.hash(password, 10);
358
- await prisma.user.create({
611
+ await prisma[userModel].create({
359
612
  data: {
360
613
  name,
361
614
  email,
@@ -376,7 +629,7 @@ function createUserHandlers(prisma, config) {
376
629
  if (!config.roles.list.includes(role)) {
377
630
  return { error: config.strings.invalidRole };
378
631
  }
379
- const existing = await prisma.user.findUnique({ where: { email } });
632
+ const existing = await prisma[userModel].findUnique({ where: { email } });
380
633
  if (existing && existing.id !== id) {
381
634
  return { error: config.strings.emailExists };
382
635
  }
@@ -391,7 +644,7 @@ function createUserHandlers(prisma, config) {
391
644
  }
392
645
  data.password = await bcrypt.hash(password, 10);
393
646
  }
394
- await prisma.user.update({
647
+ await prisma[userModel].update({
395
648
  where: { id },
396
649
  data
397
650
  });
@@ -401,29 +654,31 @@ function createUserHandlers(prisma, config) {
401
654
  if (currentUserId === id) {
402
655
  return { error: config.strings.cannotDeleteSelf };
403
656
  }
404
- await prisma.user.delete({ where: { id } });
657
+ await prisma[userModel].delete({ where: { id } });
405
658
  return { success: true };
406
659
  }
407
660
  };
408
661
  }
409
662
 
410
663
  // src/handlers/media.ts
411
- function createMediaHandlers(prisma, _config) {
664
+ function createMediaHandlers(prisma, config) {
665
+ const mediaModel = config.models.media;
666
+ const articleModel = config.models.article;
412
667
  return {
413
668
  async getMedia(search) {
414
- return prisma.media.findMany({
669
+ return prisma[mediaModel].findMany({
415
670
  where: search ? { filename: { contains: search } } : void 0,
416
671
  include: { uploader: { select: { name: true } } },
417
672
  orderBy: { createdAt: "desc" }
418
673
  });
419
674
  },
420
675
  async deleteMedia(userId, userRole, id, deleteFileFromDisk) {
421
- const media = await prisma.media.findUnique({ where: { id } });
676
+ const media = await prisma[mediaModel].findUnique({ where: { id } });
422
677
  if (!media) throw new Error("File not found");
423
678
  if (userRole === "scrittore" && media.uploaderId !== userId) {
424
679
  throw new Error("Unauthorized");
425
680
  }
426
- const usedInArticles = await prisma.article.findMany({
681
+ const usedInArticles = await prisma[articleModel].findMany({
427
682
  where: {
428
683
  OR: [
429
684
  { coverImage: media.path },
@@ -444,7 +699,7 @@ function createMediaHandlers(prisma, _config) {
444
699
  } catch {
445
700
  }
446
701
  }
447
- await prisma.media.delete({ where: { id } });
702
+ await prisma[mediaModel].delete({ where: { id } });
448
703
  return { success: true };
449
704
  }
450
705
  };
@@ -456,6 +711,7 @@ function createUploadHandler(prisma, config, storage) {
456
711
  ...config.upload.imageTypes,
457
712
  ...config.upload.videoTypes
458
713
  ];
714
+ const mediaModel = config.models.media;
459
715
  return {
460
716
  async handleUpload(file, uploaderId) {
461
717
  if (!allowedTypes.includes(file.type)) {
@@ -470,7 +726,7 @@ function createUploadHandler(prisma, config, storage) {
470
726
  }
471
727
  const buffer = Buffer.from(await file.arrayBuffer());
472
728
  const url = await storage.save(file.name, buffer, file.type);
473
- await prisma.media.create({
729
+ await prisma[mediaModel].create({
474
730
  data: {
475
731
  filename: file.name,
476
732
  path: url,
@@ -486,7 +742,8 @@ function createUploadHandler(prisma, config, storage) {
486
742
 
487
743
  // src/handlers/auth.ts
488
744
  import bcrypt2 from "bcryptjs";
489
- function buildCredentialsProvider(prisma) {
745
+ function buildCredentialsProvider(prisma, config) {
746
+ const userModel = config?.models?.user ?? "user";
490
747
  return {
491
748
  credentials: {
492
749
  email: {},
@@ -496,7 +753,7 @@ function buildCredentialsProvider(prisma) {
496
753
  const email = credentials.email;
497
754
  const password = credentials.password;
498
755
  if (!email || !password) return null;
499
- const user = await prisma.user.findUnique({
756
+ const user = await prisma[userModel].findUnique({
500
757
  where: { email }
501
758
  });
502
759
  if (!user) return null;