i18n-dashboard 0.1.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 (176) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +715 -0
  3. package/app.vue +8 -0
  4. package/assets/css/main.css +21 -0
  5. package/assets/locales/en.json +380 -0
  6. package/bin/cli.mjs +279 -0
  7. package/components/LinkedKeyPicker.vue +135 -0
  8. package/components/PathPicker.vue +153 -0
  9. package/components/PluralEditor.vue +295 -0
  10. package/components/ScanModal.vue +153 -0
  11. package/components/TranslationHistoryModal.vue +66 -0
  12. package/components/TranslationRow.vue +541 -0
  13. package/components/dashboard/WidgetConfigModal.vue +121 -0
  14. package/components/dashboard/WidgetGrid.vue +190 -0
  15. package/components/dashboard/WidgetPicker.vue +75 -0
  16. package/components/dashboard/widgets/ActivityWidget.vue +109 -0
  17. package/components/dashboard/widgets/LanguagesCoverageWidget.vue +104 -0
  18. package/components/dashboard/widgets/ProjectsWidget.vue +77 -0
  19. package/components/dashboard/widgets/ReviewWidget.vue +150 -0
  20. package/components/dashboard/widgets/StatWidget.vue +133 -0
  21. package/composables/useAuth.ts +72 -0
  22. package/composables/useConfig.ts +14 -0
  23. package/composables/useDashboard.ts +89 -0
  24. package/composables/useFormats.ts +100 -0
  25. package/composables/useKeys.ts +231 -0
  26. package/composables/useLanguages.ts +221 -0
  27. package/composables/useProfile.ts +76 -0
  28. package/composables/useProject.ts +180 -0
  29. package/composables/useReview.ts +94 -0
  30. package/composables/useSettings.ts +30 -0
  31. package/composables/useStats.ts +16 -0
  32. package/composables/useT.ts +38 -0
  33. package/composables/useUsers.ts +101 -0
  34. package/composables/useWidgetData.ts +50 -0
  35. package/consts/commons.const.ts +6 -0
  36. package/consts/dashboard.const.ts +94 -0
  37. package/consts/languages.const.ts +223 -0
  38. package/enums/commons.enum.ts +7 -0
  39. package/i18n-dashboard.config.example.js +40 -0
  40. package/interfaces/commons.interface.ts +23 -0
  41. package/interfaces/job.interface.ts +10 -0
  42. package/interfaces/key.interface.ts +39 -0
  43. package/interfaces/languages.interface.ts +23 -0
  44. package/interfaces/project.interface.ts +9 -0
  45. package/interfaces/scan.interface.ts +12 -0
  46. package/interfaces/settings.interface.ts +4 -0
  47. package/interfaces/stat.interface.ts +30 -0
  48. package/interfaces/translation.interface.ts +11 -0
  49. package/interfaces/user.interface.ts +24 -0
  50. package/layouts/auth.vue +5 -0
  51. package/layouts/default.vue +327 -0
  52. package/middleware/auth.global.ts +26 -0
  53. package/nuxt.config.ts +66 -0
  54. package/package.json +89 -0
  55. package/pages/index.vue +5 -0
  56. package/pages/login.vue +74 -0
  57. package/pages/onboarding.vue +563 -0
  58. package/pages/projects/[id]/formats/datetime.vue +240 -0
  59. package/pages/projects/[id]/formats/modifiers.vue +194 -0
  60. package/pages/projects/[id]/formats/number.vue +250 -0
  61. package/pages/projects/[id]/index.vue +182 -0
  62. package/pages/projects/[id]/languages.vue +537 -0
  63. package/pages/projects/[id]/review.vue +109 -0
  64. package/pages/projects/[id]/settings.vue +515 -0
  65. package/pages/projects/[id]/translations/[keyId].vue +642 -0
  66. package/pages/projects/[id]/translations/index.vue +250 -0
  67. package/pages/projects/[id]/users.vue +276 -0
  68. package/pages/projects/index.vue +334 -0
  69. package/pages/users/[id]/profile.vue +421 -0
  70. package/pages/users/index.vue +345 -0
  71. package/plugins/loading.client.ts +3 -0
  72. package/plugins/ui-i18n.ts +6 -0
  73. package/server/api/auth/login.post.ts +28 -0
  74. package/server/api/auth/logout.post.ts +7 -0
  75. package/server/api/auth/me.get.ts +11 -0
  76. package/server/api/auth/me.put.ts +31 -0
  77. package/server/api/auth/password.put.ts +27 -0
  78. package/server/api/auth/status.get.ts +16 -0
  79. package/server/api/config.get.ts +10 -0
  80. package/server/api/dashboard/layout.get.ts +18 -0
  81. package/server/api/dashboard/layout.post.ts +18 -0
  82. package/server/api/db-config.get.ts +44 -0
  83. package/server/api/db-config.post.ts +73 -0
  84. package/server/api/export.get.ts +64 -0
  85. package/server/api/formats/datetime/[id].delete.ts +8 -0
  86. package/server/api/formats/datetime/[id].put.ts +15 -0
  87. package/server/api/formats/datetime.get.ts +11 -0
  88. package/server/api/formats/datetime.post.ts +16 -0
  89. package/server/api/formats/modifiers/[id].delete.ts +8 -0
  90. package/server/api/formats/modifiers/[id].put.ts +10 -0
  91. package/server/api/formats/modifiers.get.ts +10 -0
  92. package/server/api/formats/modifiers.post.ts +14 -0
  93. package/server/api/formats/number/[id].delete.ts +8 -0
  94. package/server/api/formats/number/[id].put.ts +15 -0
  95. package/server/api/formats/number.get.ts +11 -0
  96. package/server/api/formats/number.post.ts +16 -0
  97. package/server/api/formats/snippet.get.ts +87 -0
  98. package/server/api/fs/browse.get.ts +50 -0
  99. package/server/api/history/[translationId].get.ts +13 -0
  100. package/server/api/keys/[id].delete.ts +14 -0
  101. package/server/api/keys/[id].get.ts +41 -0
  102. package/server/api/keys/[id].patch.ts +20 -0
  103. package/server/api/keys/index.get.ts +98 -0
  104. package/server/api/keys/index.post.ts +17 -0
  105. package/server/api/languages/[code].delete.ts +15 -0
  106. package/server/api/languages/[id].put.ts +24 -0
  107. package/server/api/languages/index.get.ts +13 -0
  108. package/server/api/languages/index.post.ts +42 -0
  109. package/server/api/onboarding.post.ts +56 -0
  110. package/server/api/profile.get.ts +81 -0
  111. package/server/api/project-snapshot.get.ts +73 -0
  112. package/server/api/project-snapshot.post.ts +160 -0
  113. package/server/api/projects/[id].delete.ts +13 -0
  114. package/server/api/projects/[id].put.ts +40 -0
  115. package/server/api/projects/index.get.ts +19 -0
  116. package/server/api/projects/index.post.ts +34 -0
  117. package/server/api/scan.post.ts +165 -0
  118. package/server/api/settings/index.get.ts +9 -0
  119. package/server/api/settings/index.post.ts +20 -0
  120. package/server/api/setup.post.ts +39 -0
  121. package/server/api/stats/global.get.ts +126 -0
  122. package/server/api/stats.get.ts +70 -0
  123. package/server/api/sync.post.ts +179 -0
  124. package/server/api/translate.post.ts +52 -0
  125. package/server/api/translations/batch-translate.post.ts +121 -0
  126. package/server/api/translations/bulk-status.post.ts +24 -0
  127. package/server/api/translations/index.post.ts +62 -0
  128. package/server/api/translations/job/[id].get.ts +23 -0
  129. package/server/api/translations/status.post.ts +30 -0
  130. package/server/api/translations/translate-all.post.ts +18 -0
  131. package/server/api/ui-locale.get.ts +39 -0
  132. package/server/api/users/[id]/profile.get.ts +107 -0
  133. package/server/api/users/[id]/roles.put.ts +67 -0
  134. package/server/api/users/[id].delete.ts +36 -0
  135. package/server/api/users/[id].put.ts +43 -0
  136. package/server/api/users/index.get.ts +49 -0
  137. package/server/api/users/index.post.ts +89 -0
  138. package/server/consts/auto-translate.const.ts +2 -0
  139. package/server/consts/commons.const.ts +10 -0
  140. package/server/consts/db.const.ts +3 -0
  141. package/server/consts/scanner.const.ts +4 -0
  142. package/server/consts/translation-job.const.ts +8 -0
  143. package/server/db/index.ts +672 -0
  144. package/server/enums/auth.enum.ts +5 -0
  145. package/server/enums/translation.enum.ts +6 -0
  146. package/server/interfaces/profile.interface.ts +48 -0
  147. package/server/interfaces/project-config.interface.ts +9 -0
  148. package/server/interfaces/scanner.interface.ts +18 -0
  149. package/server/interfaces/translation-job.interface.ts +13 -0
  150. package/server/middleware/auth.ts +32 -0
  151. package/server/plugins/db.ts +6 -0
  152. package/server/routes/locale/[lang].get.ts +179 -0
  153. package/server/types/auth.type.ts +3 -0
  154. package/server/utils/auth.util.ts +89 -0
  155. package/server/utils/auto-translate.util.ts +112 -0
  156. package/server/utils/lang-api.util.ts +24 -0
  157. package/server/utils/mailer.util.ts +80 -0
  158. package/server/utils/project-config.util.ts +37 -0
  159. package/server/utils/scanner.uti.ts +307 -0
  160. package/server/utils/translation-job.util.ts +142 -0
  161. package/services/auth.service.ts +31 -0
  162. package/services/base.service.ts +140 -0
  163. package/services/job.service.ts +10 -0
  164. package/services/key.service.ts +26 -0
  165. package/services/language.service.ts +26 -0
  166. package/services/profile.service.ts +14 -0
  167. package/services/project.service.ts +23 -0
  168. package/services/scan.service.ts +14 -0
  169. package/services/settings.service.ts +14 -0
  170. package/services/stats.service.ts +11 -0
  171. package/services/translation.service.ts +36 -0
  172. package/services/user.service.ts +28 -0
  173. package/tsconfig.json +3 -0
  174. package/types/commons.type.ts +3 -0
  175. package/types/dashboard.type.ts +26 -0
  176. package/utils/config.util.ts +60 -0
@@ -0,0 +1,89 @@
1
+ import bcrypt from 'bcryptjs'
2
+ import { getDb } from '../../db/index'
3
+ import { getUserRole, canManageUsers } from '../../utils/auth.util'
4
+ import { sendEmail, inviteEmailHtml } from '../../utils/mailer.util'
5
+ import { useRuntimeConfig } from '#imports'
6
+
7
+ function generateTempPassword(): string {
8
+ const chars = 'ABCDEFGHJKMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789'
9
+ return Array.from({ length: 12 }, () => chars[Math.floor(Math.random() * chars.length)]).join('')
10
+ }
11
+
12
+ export default defineEventHandler(async (event) => {
13
+ const currentUser = event.context.user
14
+ const body = await readBody(event)
15
+ const { name, email, role, project_ids, global_access } = body
16
+
17
+ if (!name || !email) throw createError({ statusCode: 400, message: 'Nom et email requis' })
18
+ if (!role || !['translator', 'moderator', 'admin'].includes(role)) {
19
+ throw createError({ statusCode: 400, message: 'Rôle invalide (translator | moderator | admin)' })
20
+ }
21
+
22
+ const db = getDb()
23
+
24
+ // Permission check
25
+ if (!currentUser.is_super_admin) {
26
+ // Project admin can only create users in their project
27
+ const { project_id } = body
28
+ if (!project_id) throw createError({ statusCode: 400, message: 'project_id requis' })
29
+ const userRole = await getUserRole(currentUser.id, Number(project_id))
30
+ if (!canManageUsers(userRole, false)) throw createError({ statusCode: 403, message: 'Accès refusé' })
31
+ // Project admin can't create super admins
32
+ if (body.is_super_admin) throw createError({ statusCode: 403, message: 'Accès refusé' })
33
+ }
34
+
35
+ // Check email uniqueness
36
+ const existing = await db('users').where({ email: email.toLowerCase().trim() }).first()
37
+ if (existing) throw createError({ statusCode: 409, message: 'Cet email est déjà utilisé' })
38
+
39
+ const tempPassword = generateTempPassword()
40
+ const hash = await bcrypt.hash(tempPassword, 12)
41
+
42
+ const [userId] = await db('users').insert({
43
+ name: name.trim(),
44
+ email: email.toLowerCase().trim(),
45
+ password_hash: hash,
46
+ is_super_admin: false,
47
+ is_active: true,
48
+ })
49
+
50
+ // Assign roles
51
+ if (currentUser.is_super_admin && global_access) {
52
+ // Global access: project_id = NULL
53
+ await db('user_project_roles').insert({ user_id: userId, project_id: null, role })
54
+ } else {
55
+ // Assign to specific projects
56
+ const pids: number[] = currentUser.is_super_admin
57
+ ? (project_ids || [])
58
+ : [body.project_id]
59
+
60
+ for (const pid of pids) {
61
+ await db('user_project_roles').insert({ user_id: userId, project_id: Number(pid), role })
62
+ }
63
+ }
64
+
65
+ // Send invitation email
66
+ const config = useRuntimeConfig()
67
+ const projectName = body.project_id
68
+ ? (await db('projects').where({ id: Number(body.project_id) }).first())?.name
69
+ : undefined
70
+
71
+ try {
72
+ await sendEmail({
73
+ to: email,
74
+ subject: 'Votre accès à i18n-dashboard',
75
+ html: inviteEmailHtml({
76
+ name,
77
+ email: email.toLowerCase().trim(),
78
+ tempPassword,
79
+ dashboardUrl: config.dashboardUrl as string,
80
+ projectName,
81
+ role,
82
+ }),
83
+ })
84
+ } catch (e: any) {
85
+ console.warn('[i18n-dashboard] Email non envoyé:', e.message)
86
+ }
87
+
88
+ return { id: userId, email: email.toLowerCase().trim(), name, tempPassword }
89
+ })
@@ -0,0 +1,2 @@
1
+ export const BATCH_SIZE = 30
2
+ export const BATCH_DELAY_MS = 300
@@ -0,0 +1,10 @@
1
+ export const PUBLIC_ROUTES = [
2
+ '/api/auth/login',
3
+ '/api/auth/logout',
4
+ '/api/auth/me',
5
+ '/api/auth/status',
6
+ '/api/setup',
7
+ '/api/ui-locale',
8
+ '/api/config',
9
+ '/api/db-config',
10
+ ]
@@ -0,0 +1,3 @@
1
+ import { resolve } from 'path'
2
+
3
+ export const OVERRIDE_FILE = resolve(process.cwd(), 'i18n-dashboard.db.json')
@@ -0,0 +1,4 @@
1
+ export const LOCALE_ARRAY_PATTERN = /locales\s*:\s*\[([^\]]+)\]/g
2
+ export const LOCALE_OBJECT_ARRAY_PATTERN = /locales\s*:\s*\[[\s\S]*?code\s*:\s*['"]([a-z]{2}(?:-[a-z]{2,4})?)['"]/gi
3
+ export const AVAILABLE_LOCALES_PATTERN = /availableLocales\s*:\s*\[([^\]]+)\]/g
4
+ export const LOCALE_SINGLE_PATTERN = /(?:locale|defaultLocale|fallbackLocale)\s*:\s*['"]([a-z]{2}(?:-[a-z]{2,4})?)['"]/g
@@ -0,0 +1,8 @@
1
+ export enum JobStatus {
2
+ RUNNING = 'running',
3
+ DONE = 'done',
4
+ ERROR = 'error',
5
+ }
6
+
7
+ export const JOB_BATCH_SIZE = 25
8
+ export const JOB_BATCH_DELAY_MS = 1200