@hed-hog/core 0.0.296 → 0.0.298

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 (67) hide show
  1. package/dist/auth/auth.controller.d.ts +14 -14
  2. package/dist/auth/auth.service.d.ts +14 -14
  3. package/dist/challenge/challenge.service.d.ts +2 -2
  4. package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts +15 -1
  5. package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts.map +1 -1
  6. package/dist/dashboard/dashboard-core/dashboard-core.controller.js +9 -0
  7. package/dist/dashboard/dashboard-core/dashboard-core.controller.js.map +1 -1
  8. package/dist/dashboard/dashboard-core/dashboard-core.module.d.ts.map +1 -1
  9. package/dist/dashboard/dashboard-core/dashboard-core.module.js +6 -1
  10. package/dist/dashboard/dashboard-core/dashboard-core.module.js.map +1 -1
  11. package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts +175 -3
  12. package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts.map +1 -1
  13. package/dist/dashboard/dashboard-core/dashboard-core.service.js +531 -5
  14. package/dist/dashboard/dashboard-core/dashboard-core.service.js.map +1 -1
  15. package/dist/file/file.controller.d.ts.map +1 -1
  16. package/dist/file/file.controller.js +16 -0
  17. package/dist/file/file.controller.js.map +1 -1
  18. package/dist/file/file.service.d.ts +7 -1
  19. package/dist/file/file.service.d.ts.map +1 -1
  20. package/dist/file/file.service.js +38 -1
  21. package/dist/file/file.service.js.map +1 -1
  22. package/dist/file/provider/s3.provider.d.ts +1 -0
  23. package/dist/file/provider/s3.provider.d.ts.map +1 -1
  24. package/dist/file/provider/s3.provider.js +38 -29
  25. package/dist/file/provider/s3.provider.js.map +1 -1
  26. package/dist/oauth/oauth.service.d.ts.map +1 -1
  27. package/dist/oauth/oauth.service.js +2 -1
  28. package/dist/oauth/oauth.service.js.map +1 -1
  29. package/dist/profile/profile.controller.d.ts +6 -6
  30. package/dist/profile/profile.service.d.ts +6 -6
  31. package/dist/session/session.controller.d.ts +2 -2
  32. package/dist/session/session.service.d.ts +3 -3
  33. package/dist/setting/setting.controller.d.ts +9 -9
  34. package/dist/setting/setting.service.d.ts +10 -10
  35. package/dist/user/constants/user.constants.d.ts +1 -0
  36. package/dist/user/constants/user.constants.d.ts.map +1 -1
  37. package/dist/user/constants/user.constants.js +2 -1
  38. package/dist/user/constants/user.constants.js.map +1 -1
  39. package/dist/user/user.controller.d.ts +15 -15
  40. package/dist/user/user.service.d.ts +39 -39
  41. package/dist/user/user.service.d.ts.map +1 -1
  42. package/dist/user/user.service.js +2 -1
  43. package/dist/user/user.service.js.map +1 -1
  44. package/hedhog/data/dashboard_item.yaml +11 -11
  45. package/hedhog/data/route.yaml +8 -0
  46. package/hedhog/frontend/app/dashboard/[slug]/dashboard-content.tsx.ejs +76 -15
  47. package/hedhog/frontend/app/dashboard/components/widgets/email-notifications.tsx.ejs +85 -61
  48. package/hedhog/frontend/app/dashboard/components/widgets/locale-config.tsx.ejs +139 -280
  49. package/hedhog/frontend/app/dashboard/components/widgets/mail-config.tsx.ejs +161 -407
  50. package/hedhog/frontend/app/dashboard/components/widgets/oauth-config.tsx.ejs +150 -271
  51. package/hedhog/frontend/app/dashboard/components/widgets/profile-card.tsx.ejs +3 -3
  52. package/hedhog/frontend/app/dashboard/components/widgets/storage-config.tsx.ejs +161 -305
  53. package/hedhog/frontend/app/dashboard/components/widgets/theme-config.tsx.ejs +184 -246
  54. package/hedhog/frontend/app/dashboard/components/widgets/user-roles.tsx.ejs +12 -14
  55. package/hedhog/frontend/messages/en.json +90 -0
  56. package/hedhog/frontend/messages/pt.json +90 -0
  57. package/hedhog/table/mail_sent_user.yaml +75 -0
  58. package/package.json +4 -4
  59. package/src/dashboard/dashboard-core/dashboard-core.controller.ts +5 -0
  60. package/src/dashboard/dashboard-core/dashboard-core.module.ts +6 -1
  61. package/src/dashboard/dashboard-core/dashboard-core.service.ts +766 -3
  62. package/src/file/file.controller.ts +37 -13
  63. package/src/file/file.service.ts +47 -5
  64. package/src/file/provider/s3.provider.ts +39 -29
  65. package/src/oauth/oauth.service.ts +8 -7
  66. package/src/user/constants/user.constants.ts +1 -0
  67. package/src/user/user.service.ts +2 -1
@@ -1,7 +1,6 @@
1
1
  'use client';
2
2
 
3
3
  import { Badge } from '@/components/ui/badge';
4
- import { Button } from '@/components/ui/button';
5
4
  import {
6
5
  Card,
7
6
  CardContent,
@@ -9,332 +8,189 @@ import {
9
8
  CardHeader,
10
9
  CardTitle,
11
10
  } from '@/components/ui/card';
12
- import { Input } from '@/components/ui/input';
13
- import { Label } from '@/components/ui/label';
14
- import {
15
- Select,
16
- SelectContent,
17
- SelectItem,
18
- SelectTrigger,
19
- SelectValue,
20
- } from '@/components/ui/select';
21
- import {
22
- AlertCircle,
23
- CheckCircle2,
24
- Cloud,
25
- FolderOpen,
26
- HardDrive,
27
- } from 'lucide-react';
28
- import { useState } from 'react';
29
-
30
- type StorageProvider = 'local' | 's3' | 'gcs' | 'azure';
11
+ import { useWidgetData } from '@/hooks/use-widget-data';
12
+ import type {
13
+ DashboardCoreConfigOverviewData,
14
+ StorageConfigWidgetData,
15
+ } from '@/types/widget-data';
16
+ import { CheckCircle2, HardDrive } from 'lucide-react';
17
+ import { useTranslations } from 'next-intl';
18
+ import { WidgetWrapper } from '../widget-wrapper';
31
19
 
32
- const providerList: {
33
- value: StorageProvider;
34
- label: string;
35
- icon: React.ReactNode;
36
- description: string;
37
- }[] = [
38
- {
39
- value: 'local',
40
- label: 'Local',
41
- icon: <FolderOpen className="h-4 w-4" />,
42
- description: 'Disco do servidor',
43
- },
44
- {
45
- value: 's3',
46
- label: 'AWS S3',
47
- icon: <Cloud className="h-4 w-4" />,
48
- description: 'Amazon S3 Bucket',
49
- },
50
- {
51
- value: 'gcs',
52
- label: 'Google Cloud',
53
- icon: <Cloud className="h-4 w-4" />,
54
- description: 'Google Cloud Storage',
20
+ const defaultStorageConfigData: StorageConfigWidgetData = {
21
+ status: {
22
+ isConfigured: false,
23
+ totalProfiles: 0,
24
+ activeProfiles: 0,
25
+ defaultProfileId: null,
55
26
  },
56
- {
57
- value: 'azure',
58
- label: 'Azure Blob',
59
- icon: <Cloud className="h-4 w-4" />,
60
- description: 'Azure Blob Storage',
61
- },
62
- ];
27
+ providers: [],
28
+ profiles: [],
29
+ };
63
30
 
64
- export default function StorageConfig() {
65
- const [provider, setProvider] = useState<StorageProvider>('local');
66
- const [localPath, setLocalPath] = useState('/var/data/uploads');
67
- const [localMaxSize, setLocalMaxSize] = useState('50');
31
+ interface StorageConfigProps {
32
+ widget?: { name?: string };
33
+ onRemove?: () => void;
34
+ }
68
35
 
69
- const [s3Bucket, setS3Bucket] = useState('');
70
- const [s3Region, setS3Region] = useState('us-east-1');
71
- const [s3AccessKey, setS3AccessKey] = useState('');
72
- const [s3SecretKey, setS3SecretKey] = useState('');
73
- const [s3Prefix, setS3Prefix] = useState('uploads/');
36
+ export default function StorageConfig({
37
+ widget,
38
+ onRemove,
39
+ }: StorageConfigProps) {
40
+ const t = useTranslations('core.DashboardPage.storageConfig');
74
41
 
75
- const [gcsBucket, setGcsBucket] = useState('');
76
- const [gcsProjectId, setGcsProjectId] = useState('');
77
- const [gcsKeyFile, setGcsKeyFile] = useState('');
42
+ const { data, isLoading, isError, isAccessDenied } = useWidgetData<
43
+ DashboardCoreConfigOverviewData,
44
+ StorageConfigWidgetData
45
+ >({
46
+ endpoint: '/dashboard-core/config/overview',
47
+ queryKey: 'dashboard-core-config-overview',
48
+ select: (d) => d.storageConfig,
49
+ });
78
50
 
79
- const [azureAccount, setAzureAccount] = useState('');
80
- const [azureKey, setAzureKey] = useState('');
81
- const [azureContainer, setAzureContainer] = useState('');
82
- const [azureEndpoint, setAzureEndpoint] = useState('');
51
+ const storageData = data ?? defaultStorageConfigData;
52
+ const defaultProfile = storageData.profiles.find(
53
+ (profile) => profile.id === storageData.status.defaultProfileId
54
+ );
83
55
 
84
56
  return (
85
- <Card className="h-full">
86
- <CardHeader>
87
- <div className="flex items-center justify-between">
88
- <div className="flex items-center gap-3">
89
- <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-violet-50">
90
- <HardDrive className="h-5 w-5 text-violet-600" />
91
- </div>
92
- <div>
93
- <CardTitle className="text-base">
94
- Armazenamento de Arquivos
95
- </CardTitle>
96
- <CardDescription>
97
- Configure onde os arquivos do sistema serao armazenados
98
- </CardDescription>
99
- </div>
100
- </div>
101
- <Badge variant="secondary" className="bg-emerald-50 text-emerald-700">
102
- <CheckCircle2 className="mr-1 h-3 w-3" />
103
- Configurado
104
- </Badge>
105
- </div>
106
- </CardHeader>
107
- <CardContent className="space-y-6">
108
- {/* Provider Selector */}
109
- <div className="grid grid-cols-2 gap-3 sm:grid-cols-4">
110
- {providerList.map((p) => (
111
- <button
112
- key={p.value}
113
- type="button"
114
- onClick={() => setProvider(p.value)}
115
- className={`flex flex-col items-center gap-1.5 rounded-lg border-2 p-3 text-center transition-all ${
116
- provider === p.value
117
- ? 'border-foreground bg-foreground/3'
118
- : 'border-border hover:border-foreground/20'
119
- }`}
120
- >
121
- <div
122
- className={`flex h-8 w-8 items-center justify-center rounded-full ${
123
- provider === p.value
124
- ? 'bg-foreground text-background'
125
- : 'bg-muted text-muted-foreground'
126
- }`}
127
- >
128
- {p.icon}
57
+ <WidgetWrapper
58
+ isLoading={isLoading}
59
+ isError={isError}
60
+ isAccessDenied={isAccessDenied}
61
+ widgetName={widget?.name ?? t('title')}
62
+ onRemove={onRemove}
63
+ >
64
+ <Card className="flex h-full min-h-0 flex-col overflow-hidden">
65
+ <CardHeader className="shrink-0">
66
+ <div className="flex items-center justify-between gap-3">
67
+ <div className="flex items-center gap-3">
68
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-violet-50">
69
+ <HardDrive className="h-5 w-5 text-violet-600" />
129
70
  </div>
130
- <span className="text-xs font-medium">{p.label}</span>
131
- </button>
132
- ))}
133
- </div>
134
-
135
- {/* Local Fields */}
136
- {provider === 'local' && (
137
- <div className="space-y-4">
138
- <div className="space-y-2">
139
- <Label htmlFor="local-path">Caminho do diretorio</Label>
140
- <Input
141
- id="local-path"
142
- value={localPath}
143
- onChange={(e) => setLocalPath(e.target.value)}
144
- placeholder="/var/data/uploads"
145
- className="font-mono text-sm"
146
- />
147
- </div>
148
- <div className="space-y-2">
149
- <Label htmlFor="local-max-size">
150
- Tamanho maximo por arquivo (MB)
151
- </Label>
152
- <Input
153
- id="local-max-size"
154
- type="number"
155
- value={localMaxSize}
156
- onChange={(e) => setLocalMaxSize(e.target.value)}
157
- />
158
- </div>
159
- <div className="rounded-lg border border-blue-200 bg-blue-50 p-3">
160
- <div className="flex items-start gap-2">
161
- <AlertCircle className="mt-0.5 h-4 w-4 text-blue-600" />
162
- <p className="text-xs text-blue-800">
163
- Certifique-se de que o diretorio existe e que o servidor tem
164
- permissoes de leitura e escrita.
165
- </p>
71
+ <div>
72
+ <CardTitle className="text-base">{t('title')}</CardTitle>
73
+ <CardDescription>{t('description')}</CardDescription>
166
74
  </div>
167
75
  </div>
76
+ <Badge
77
+ variant="secondary"
78
+ className={
79
+ storageData.status.isConfigured
80
+ ? 'bg-emerald-50 text-emerald-700'
81
+ : 'bg-amber-50 text-amber-700'
82
+ }
83
+ >
84
+ {storageData.status.isConfigured ? t('configured') : t('pending')}
85
+ </Badge>
168
86
  </div>
169
- )}
170
-
171
- {/* AWS S3 Fields */}
172
- {provider === 's3' && (
173
- <div className="space-y-4">
174
- <div className="grid gap-4 sm:grid-cols-2">
175
- <div className="space-y-2">
176
- <Label htmlFor="s3-bucket">Bucket</Label>
177
- <Input
178
- id="s3-bucket"
179
- value={s3Bucket}
180
- onChange={(e) => setS3Bucket(e.target.value)}
181
- placeholder="meu-bucket"
182
- className="font-mono text-sm"
183
- />
184
- </div>
185
- <div className="space-y-2">
186
- <Label>Regiao</Label>
187
- <Select value={s3Region} onValueChange={setS3Region}>
188
- <SelectTrigger>
189
- <SelectValue />
190
- </SelectTrigger>
191
- <SelectContent>
192
- <SelectItem value="us-east-1">
193
- US East (N. Virginia)
194
- </SelectItem>
195
- <SelectItem value="us-west-2">US West (Oregon)</SelectItem>
196
- <SelectItem value="eu-west-1">EU (Ireland)</SelectItem>
197
- <SelectItem value="eu-central-1">EU (Frankfurt)</SelectItem>
198
- <SelectItem value="sa-east-1">
199
- South America (Sao Paulo)
200
- </SelectItem>
201
- </SelectContent>
202
- </Select>
203
- </div>
204
- </div>
205
- <div className="space-y-2">
206
- <Label htmlFor="s3-access-key">Access Key ID</Label>
207
- <Input
208
- id="s3-access-key"
209
- value={s3AccessKey}
210
- onChange={(e) => setS3AccessKey(e.target.value)}
211
- placeholder="AKIAIOSFODNN7EXAMPLE"
212
- className="font-mono text-sm"
213
- />
87
+ </CardHeader>
88
+ <CardContent className="flex min-h-0 flex-1 flex-col gap-4 overflow-hidden pt-0">
89
+ <div className="grid grid-cols-3 gap-2">
90
+ <div className="rounded-lg border bg-muted/30 p-3 text-center">
91
+ <p className="text-[11px] text-muted-foreground">
92
+ {t('profiles')}
93
+ </p>
94
+ <p className="mt-1 text-lg font-semibold">
95
+ {storageData.status.totalProfiles}
96
+ </p>
214
97
  </div>
215
- <div className="space-y-2">
216
- <Label htmlFor="s3-secret-key">Secret Access Key</Label>
217
- <Input
218
- id="s3-secret-key"
219
- type="password"
220
- value={s3SecretKey}
221
- onChange={(e) => setS3SecretKey(e.target.value)}
222
- className="font-mono text-sm"
223
- />
98
+ <div className="rounded-lg border bg-muted/30 p-3 text-center">
99
+ <p className="text-[11px] text-muted-foreground">{t('active')}</p>
100
+ <p className="mt-1 text-lg font-semibold">
101
+ {storageData.status.activeProfiles}
102
+ </p>
224
103
  </div>
225
- <div className="space-y-2">
226
- <Label htmlFor="s3-prefix">Prefixo (pasta)</Label>
227
- <Input
228
- id="s3-prefix"
229
- value={s3Prefix}
230
- onChange={(e) => setS3Prefix(e.target.value)}
231
- placeholder="uploads/"
232
- className="font-mono text-sm"
233
- />
104
+ <div className="rounded-lg border bg-muted/30 p-3 text-center">
105
+ <p className="text-[11px] text-muted-foreground">
106
+ {t('providers')}
107
+ </p>
108
+ <p className="mt-1 text-lg font-semibold">
109
+ {storageData.providers.length}
110
+ </p>
234
111
  </div>
235
112
  </div>
236
- )}
237
113
 
238
- {/* Google Cloud Storage Fields */}
239
- {provider === 'gcs' && (
240
- <div className="space-y-4">
241
- <div className="grid gap-4 sm:grid-cols-2">
242
- <div className="space-y-2">
243
- <Label htmlFor="gcs-project-id">Project ID</Label>
244
- <Input
245
- id="gcs-project-id"
246
- value={gcsProjectId}
247
- onChange={(e) => setGcsProjectId(e.target.value)}
248
- placeholder="meu-projeto-123"
249
- className="font-mono text-sm"
250
- />
251
- </div>
252
- <div className="space-y-2">
253
- <Label htmlFor="gcs-bucket">Bucket</Label>
254
- <Input
255
- id="gcs-bucket"
256
- value={gcsBucket}
257
- onChange={(e) => setGcsBucket(e.target.value)}
258
- placeholder="meu-bucket-gcs"
259
- className="font-mono text-sm"
260
- />
261
- </div>
262
- </div>
263
- <div className="space-y-2">
264
- <Label htmlFor="gcs-key-file">
265
- Chave JSON da Service Account
266
- </Label>
267
- <textarea
268
- id="gcs-key-file"
269
- className="flex min-h-[100px] w-full rounded-md border border-input bg-background px-3 py-2 font-mono text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
270
- value={gcsKeyFile}
271
- onChange={(e) => setGcsKeyFile(e.target.value)}
272
- placeholder='{"type": "service_account", ...}'
273
- />
274
- </div>
114
+ <div className="rounded-lg border p-3">
115
+ <p className="mb-1 text-xs text-muted-foreground">
116
+ {t('defaultProfile')}
117
+ </p>
118
+ <p className="truncate text-sm font-medium">
119
+ {defaultProfile?.name || t('notSet')}
120
+ </p>
121
+ {defaultProfile && (
122
+ <p className="mt-1 text-[11px] text-muted-foreground">
123
+ {defaultProfile.providerType.toUpperCase()} |{' '}
124
+ {defaultProfile.bucketName}
125
+ </p>
126
+ )}
275
127
  </div>
276
- )}
277
128
 
278
- {/* Azure Blob Storage Fields */}
279
- {provider === 'azure' && (
280
- <div className="space-y-4">
281
- <div className="grid gap-4 sm:grid-cols-2">
282
- <div className="space-y-2">
283
- <Label htmlFor="azure-account">Storage Account Name</Label>
284
- <Input
285
- id="azure-account"
286
- value={azureAccount}
287
- onChange={(e) => setAzureAccount(e.target.value)}
288
- placeholder="meuaccount"
289
- className="font-mono text-sm"
290
- />
291
- </div>
292
- <div className="space-y-2">
293
- <Label htmlFor="azure-container">Container</Label>
294
- <Input
295
- id="azure-container"
296
- value={azureContainer}
297
- onChange={(e) => setAzureContainer(e.target.value)}
298
- placeholder="arquivos"
299
- className="font-mono text-sm"
300
- />
129
+ <div className="grid gap-2 sm:grid-cols-2">
130
+ {storageData.providers.map((provider) => (
131
+ <div
132
+ key={provider.providerType}
133
+ className="rounded-lg border p-3"
134
+ >
135
+ <div className="flex items-center justify-between">
136
+ <p className="text-sm font-medium">
137
+ {provider.providerType.toUpperCase()}
138
+ </p>
139
+ <Badge variant="outline" className="text-[10px]">
140
+ {provider.defaults} {t('defaults')}
141
+ </Badge>
142
+ </div>
143
+ <p className="mt-1 text-[11px] text-muted-foreground">
144
+ {t('providerSummary', {
145
+ total: provider.total,
146
+ active: provider.active,
147
+ })}
148
+ </p>
301
149
  </div>
302
- </div>
303
- <div className="space-y-2">
304
- <Label htmlFor="azure-key">Access Key</Label>
305
- <Input
306
- id="azure-key"
307
- type="password"
308
- value={azureKey}
309
- onChange={(e) => setAzureKey(e.target.value)}
310
- className="font-mono text-sm"
311
- />
312
- </div>
313
- <div className="space-y-2">
314
- <Label htmlFor="azure-endpoint">
315
- Endpoint personalizado (opcional)
316
- </Label>
317
- <Input
318
- id="azure-endpoint"
319
- value={azureEndpoint}
320
- onChange={(e) => setAzureEndpoint(e.target.value)}
321
- placeholder="https://meuaccount.blob.core.windows.net"
322
- className="font-mono text-sm"
323
- />
324
- </div>
150
+ ))}
325
151
  </div>
326
- )}
327
152
 
328
- {/* Actions */}
329
- <div className="flex items-center justify-end border-t pt-4">
330
- <div className="flex gap-2">
331
- <Button variant="outline" size="sm">
332
- Testar conexao
333
- </Button>
334
- <Button size="sm">Salvar</Button>
153
+ <div className="min-h-0 flex-1 space-y-2 overflow-auto">
154
+ {storageData.profiles.slice(0, 4).map((profile) => (
155
+ <div key={profile.id} className="rounded-lg border p-3">
156
+ <div className="flex items-center justify-between gap-2">
157
+ <p className="truncate text-sm font-medium">{profile.name}</p>
158
+ <div className="flex items-center gap-1.5">
159
+ {profile.isDefault && (
160
+ <Badge variant="outline" className="text-[10px]">
161
+ {t('defaultTag')}
162
+ </Badge>
163
+ )}
164
+ <Badge
165
+ variant="secondary"
166
+ className={
167
+ profile.isActive
168
+ ? 'bg-emerald-50 text-emerald-700'
169
+ : 'bg-muted text-muted-foreground'
170
+ }
171
+ >
172
+ {profile.isActive ? (
173
+ <>
174
+ <CheckCircle2 className="mr-1 h-3 w-3" />
175
+ {t('active')}
176
+ </>
177
+ ) : (
178
+ t('inactive')
179
+ )}
180
+ </Badge>
181
+ </div>
182
+ </div>
183
+ <p className="mt-1 truncate text-[11px] text-muted-foreground">
184
+ {profile.providerType.toUpperCase()} | {profile.bucketName}
185
+ </p>
186
+ </div>
187
+ ))}
188
+ {storageData.profiles.length === 0 && (
189
+ <p className="text-xs text-muted-foreground">{t('noProfiles')}</p>
190
+ )}
335
191
  </div>
336
- </div>
337
- </CardContent>
338
- </Card>
192
+ </CardContent>
193
+ </Card>
194
+ </WidgetWrapper>
339
195
  );
340
196
  }