@hed-hog/core 0.0.373 → 0.0.374
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.
- package/README.md +83 -1
- package/dist/integration-profile/integration-profile.secrets.d.ts.map +1 -1
- package/dist/integration-profile/integration-profile.secrets.js +4 -0
- package/dist/integration-profile/integration-profile.secrets.js.map +1 -1
- package/dist/integration-profile/integration-profile.utils.d.ts +6 -0
- package/dist/integration-profile/integration-profile.utils.d.ts.map +1 -1
- package/dist/integration-profile/integration-profile.utils.js +1 -1
- package/dist/integration-profile/integration-profile.utils.js.map +1 -1
- package/dist/oauth/oauth.controller.d.ts +11 -3
- package/dist/oauth/oauth.controller.d.ts.map +1 -1
- package/dist/oauth/oauth.controller.js +52 -14
- package/dist/oauth/oauth.controller.js.map +1 -1
- package/dist/oauth/oauth.module.d.ts.map +1 -1
- package/dist/oauth/oauth.module.js +4 -0
- package/dist/oauth/oauth.module.js.map +1 -1
- package/dist/oauth/oauth.service.d.ts +38 -2
- package/dist/oauth/oauth.service.d.ts.map +1 -1
- package/dist/oauth/oauth.service.js +122 -5
- package/dist/oauth/oauth.service.js.map +1 -1
- package/dist/oauth/providers/apple.provider.d.ts +34 -0
- package/dist/oauth/providers/apple.provider.d.ts.map +1 -0
- package/dist/oauth/providers/apple.provider.js +197 -0
- package/dist/oauth/providers/apple.provider.js.map +1 -0
- package/dist/oauth/providers/facebook.provider.d.ts +1 -1
- package/dist/oauth/providers/facebook.provider.d.ts.map +1 -1
- package/dist/oauth/providers/facebook.provider.js +3 -2
- package/dist/oauth/providers/facebook.provider.js.map +1 -1
- package/dist/oauth/providers/google.provider.d.ts +1 -1
- package/dist/oauth/providers/google.provider.d.ts.map +1 -1
- package/dist/oauth/providers/google.provider.js +3 -2
- package/dist/oauth/providers/google.provider.js.map +1 -1
- package/dist/oauth/providers/linkedin.provider.d.ts +25 -0
- package/dist/oauth/providers/linkedin.provider.d.ts.map +1 -0
- package/dist/oauth/providers/linkedin.provider.js +99 -0
- package/dist/oauth/providers/linkedin.provider.js.map +1 -0
- package/dist/oauth/providers/microsoft-entra-id.provider.js +1 -1
- package/dist/oauth/providers/microsoft-entra-id.provider.js.map +1 -1
- package/dist/oauth/providers/microsoft.provider.d.ts +1 -1
- package/dist/oauth/providers/microsoft.provider.d.ts.map +1 -1
- package/dist/oauth/providers/microsoft.provider.js +3 -2
- package/dist/oauth/providers/microsoft.provider.js.map +1 -1
- package/dist/pdf/pdf.service.d.ts.map +1 -1
- package/dist/pdf/pdf.service.js +19 -2
- package/dist/pdf/pdf.service.js.map +1 -1
- package/dist/setting/setting.service.d.ts.map +1 -1
- package/dist/setting/setting.service.js +4 -1
- package/dist/setting/setting.service.js.map +1 -1
- package/hedhog/data/integration_provider.yaml +16 -0
- package/hedhog/data/setting_group.yaml +98 -0
- package/hedhog/data/setting_subgroup.yaml +16 -0
- package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +2 -0
- package/hedhog/frontend/app/dashboard/dashboard.css.ejs +11 -11
- package/hedhog/frontend/app/integration/profiles/page.tsx.ejs +42 -0
- package/hedhog/frontend/app/menu/page.tsx.ejs +1 -1
- package/hedhog/frontend/app/users/user-sheet.tsx.ejs +8 -0
- package/hedhog/frontend/messages/en.json +35 -0
- package/hedhog/frontend/messages/pt.json +35 -0
- package/hedhog/table/user_account.yaml +1 -0
- package/package.json +3 -3
- package/src/access-log/access-log.service.spec.ts +235 -0
- package/src/ai-instruction/ai-prompt-builder.service.spec.ts +250 -0
- package/src/auth/auth.service.spec.ts +891 -0
- package/src/auth/guards/auth.guard.spec.ts +177 -0
- package/src/challenge/challenge.service.spec.ts +334 -0
- package/src/dashboard/dashboard-core/dashboard-core.service.spec.ts +193 -0
- package/src/file/image-optimization.service.spec.ts +283 -0
- package/src/file/provider/provider.factory.spec.ts +111 -0
- package/src/install/install.service.spec.ts +139 -0
- package/src/integration/services/event-subscriber.registry.spec.ts +143 -0
- package/src/integration/services/inbox.service.spec.ts +183 -0
- package/src/integration/services/integration-link.service.spec.ts +217 -0
- package/src/integration/services/outbox.service.spec.ts +290 -0
- package/src/integration-profile/integration-credential-crypto.service.spec.ts +289 -0
- package/src/integration-profile/integration-profile.secrets.ts +4 -0
- package/src/integration-profile/integration-profile.utils.spec.ts +332 -0
- package/src/integration-profile/integration-profile.utils.ts +14 -1
- package/src/locale/locale.service.spec.ts +78 -0
- package/src/mail/mail.service.spec.ts +257 -0
- package/src/mcp/guards/mcp-auth.guard.spec.ts +87 -0
- package/src/mcp/guards/mcp-permission.guard.spec.ts +64 -0
- package/src/mcp/mcp-api-key.service.spec.ts +125 -0
- package/src/mcp/mcp-registry.service.spec.ts +96 -0
- package/src/mcp/mcp-server.service.spec.ts +187 -0
- package/src/menu/menu.service.spec.ts +76 -0
- package/src/notification/notification.service.spec.ts +297 -0
- package/src/oauth/oauth.controller.ts +53 -10
- package/src/oauth/oauth.module.ts +4 -0
- package/src/oauth/oauth.service.spec.ts +332 -0
- package/src/oauth/oauth.service.ts +156 -1
- package/src/oauth/providers/apple.provider.ts +175 -0
- package/src/oauth/providers/facebook.provider.ts +3 -2
- package/src/oauth/providers/google.provider.ts +3 -2
- package/src/oauth/providers/linkedin.provider.ts +96 -0
- package/src/oauth/providers/microsoft-entra-id.provider.ts +1 -1
- package/src/oauth/providers/microsoft.provider.ts +3 -2
- package/src/pdf/pdf.service.ts +20 -2
- package/src/role/guards/role.guard.spec.ts +157 -0
- package/src/role/role.service.spec.ts +179 -0
- package/src/session/session.service.spec.ts +376 -0
- package/src/setting/setting.service.spec.ts +316 -0
- package/src/setting/setting.service.ts +4 -1
- package/src/task/task.service.spec.ts +47 -0
- package/src/token/token.service.spec.ts +351 -0
- package/src/user/user.service.spec.ts +256 -0
- package/src/utils/locale-context.spec.ts +22 -0
- package/src/utils/webauthn-origin.spec.ts +73 -0
- package/src/webhook-integration/integration-event-catalog.service.spec.ts +149 -0
|
@@ -981,6 +981,104 @@
|
|
|
981
981
|
subgroup_id:
|
|
982
982
|
where:
|
|
983
983
|
slug: microsoft-entra-id
|
|
984
|
+
- slug: oauth-apple-profile-id
|
|
985
|
+
type: string
|
|
986
|
+
component: entity-picker
|
|
987
|
+
name:
|
|
988
|
+
en: Apple OAuth Profile
|
|
989
|
+
pt: Perfil OAuth Apple
|
|
990
|
+
description:
|
|
991
|
+
en: Integration profile (type OAuth / Apple) used for Sign in with Apple
|
|
992
|
+
pt: Perfil de integração (tipo OAuth / Apple) utilizado para autenticação com a Apple
|
|
993
|
+
value: ""
|
|
994
|
+
user_override: false
|
|
995
|
+
subgroup_id:
|
|
996
|
+
where:
|
|
997
|
+
slug: apple
|
|
998
|
+
- slug: oauth-apple-enabled
|
|
999
|
+
type: boolean
|
|
1000
|
+
component: switch
|
|
1001
|
+
name:
|
|
1002
|
+
en: Enable Apple
|
|
1003
|
+
pt: Habilitar Apple
|
|
1004
|
+
description:
|
|
1005
|
+
en: Enable or disable Sign in with Apple without removing the configured profile
|
|
1006
|
+
pt: Habilitar ou desabilitar login com Apple sem remover o perfil configurado
|
|
1007
|
+
value: 'false'
|
|
1008
|
+
user_override: false
|
|
1009
|
+
subgroup_id:
|
|
1010
|
+
where:
|
|
1011
|
+
slug: apple
|
|
1012
|
+
- slug: oauth-apple-scopes
|
|
1013
|
+
type: array
|
|
1014
|
+
component: checkbox
|
|
1015
|
+
name:
|
|
1016
|
+
en: OAuth Scopes
|
|
1017
|
+
pt: Escopos OAuth
|
|
1018
|
+
description:
|
|
1019
|
+
en: Permissions requested from the user during Sign in with Apple
|
|
1020
|
+
pt: Permissões solicitadas ao usuário durante o fluxo de login com a Apple
|
|
1021
|
+
value: '["name","email"]'
|
|
1022
|
+
user_override: false
|
|
1023
|
+
relations:
|
|
1024
|
+
setting_list:
|
|
1025
|
+
- value: name
|
|
1026
|
+
order: 0
|
|
1027
|
+
- value: email
|
|
1028
|
+
order: 1
|
|
1029
|
+
subgroup_id:
|
|
1030
|
+
where:
|
|
1031
|
+
slug: apple
|
|
1032
|
+
- slug: oauth-linkedin-profile-id
|
|
1033
|
+
type: string
|
|
1034
|
+
component: entity-picker
|
|
1035
|
+
name:
|
|
1036
|
+
en: LinkedIn OAuth Profile
|
|
1037
|
+
pt: Perfil OAuth LinkedIn
|
|
1038
|
+
description:
|
|
1039
|
+
en: Integration profile (type OAuth / LinkedIn) used for LinkedIn authentication
|
|
1040
|
+
pt: Perfil de integração (tipo OAuth / LinkedIn) utilizado para autenticação com o LinkedIn
|
|
1041
|
+
value: ""
|
|
1042
|
+
user_override: false
|
|
1043
|
+
subgroup_id:
|
|
1044
|
+
where:
|
|
1045
|
+
slug: linkedin
|
|
1046
|
+
- slug: oauth-linkedin-enabled
|
|
1047
|
+
type: boolean
|
|
1048
|
+
component: switch
|
|
1049
|
+
name:
|
|
1050
|
+
en: Enable LinkedIn
|
|
1051
|
+
pt: Habilitar LinkedIn
|
|
1052
|
+
description:
|
|
1053
|
+
en: Enable or disable LinkedIn OAuth without removing the configured profile
|
|
1054
|
+
pt: Habilitar ou desabilitar OAuth do LinkedIn sem remover o perfil configurado
|
|
1055
|
+
value: 'false'
|
|
1056
|
+
user_override: false
|
|
1057
|
+
subgroup_id:
|
|
1058
|
+
where:
|
|
1059
|
+
slug: linkedin
|
|
1060
|
+
- slug: oauth-linkedin-scopes
|
|
1061
|
+
type: array
|
|
1062
|
+
component: checkbox
|
|
1063
|
+
name:
|
|
1064
|
+
en: OAuth Scopes
|
|
1065
|
+
pt: Escopos OAuth
|
|
1066
|
+
description:
|
|
1067
|
+
en: Permissions requested from the user during LinkedIn OAuth flow
|
|
1068
|
+
pt: Permissões solicitadas ao usuário durante o fluxo OAuth do LinkedIn
|
|
1069
|
+
value: '["openid","profile","email"]'
|
|
1070
|
+
user_override: false
|
|
1071
|
+
relations:
|
|
1072
|
+
setting_list:
|
|
1073
|
+
- value: openid
|
|
1074
|
+
order: 0
|
|
1075
|
+
- value: profile
|
|
1076
|
+
order: 1
|
|
1077
|
+
- value: email
|
|
1078
|
+
order: 2
|
|
1079
|
+
subgroup_id:
|
|
1080
|
+
where:
|
|
1081
|
+
slug: linkedin
|
|
984
1082
|
- slug: appearance
|
|
985
1083
|
icon: palette
|
|
986
1084
|
name:
|
|
@@ -182,6 +182,22 @@
|
|
|
182
182
|
en: Microsoft Entra ID (Azure AD) OAuth Provider
|
|
183
183
|
pt: Provedor OAuth Microsoft Entra ID (Azure AD)
|
|
184
184
|
|
|
185
|
+
- slug: apple
|
|
186
|
+
name:
|
|
187
|
+
en: Apple
|
|
188
|
+
pt: Apple
|
|
189
|
+
description:
|
|
190
|
+
en: Apple OAuth Provider (Sign in with Apple)
|
|
191
|
+
pt: Provedor OAuth da Apple (Sign in with Apple)
|
|
192
|
+
|
|
193
|
+
- slug: linkedin
|
|
194
|
+
name:
|
|
195
|
+
en: LinkedIn
|
|
196
|
+
pt: LinkedIn
|
|
197
|
+
description:
|
|
198
|
+
en: LinkedIn OAuth Provider
|
|
199
|
+
pt: Provedor OAuth do LinkedIn
|
|
200
|
+
|
|
185
201
|
- slug: theme-selector
|
|
186
202
|
name:
|
|
187
203
|
en: Theme Selector
|
|
@@ -109,6 +109,8 @@ const OAUTH_PROFILE_SLUG_TO_PROVIDER: Record<string, string> = {
|
|
|
109
109
|
'oauth-github-profile-id': 'github-oauth',
|
|
110
110
|
'oauth-microsoft-profile-id': 'microsoft-oauth',
|
|
111
111
|
'oauth-microsoft-entra-id-profile-id': 'microsoft-entra-id-oauth',
|
|
112
|
+
'oauth-apple-profile-id': 'apple-oauth',
|
|
113
|
+
'oauth-linkedin-profile-id': 'linkedin-oauth',
|
|
112
114
|
};
|
|
113
115
|
// Settings cujo valor é o SLUG de um integration_profile. O picker continua sendo
|
|
114
116
|
// selecionado/exibido pelo nome; apenas o valor persistido é o slug.
|
|
@@ -12,27 +12,27 @@
|
|
|
12
12
|
|
|
13
13
|
.dashboard-tabs-list .dashboard-tab-trigger.dashboard-tab-trigger-inactive {
|
|
14
14
|
background: transparent !important;
|
|
15
|
-
color:
|
|
15
|
+
color: color-mix(in oklab, var(--muted-foreground) 60%, transparent) !important;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
.dashboard-tabs-list
|
|
19
19
|
.dashboard-tab-trigger.dashboard-tab-trigger-inactive:hover {
|
|
20
|
-
background:
|
|
21
|
-
color:
|
|
20
|
+
background: color-mix(in oklab, var(--background) 50%, transparent) !important;
|
|
21
|
+
color: color-mix(in oklab, var(--foreground) 80%, transparent) !important;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
.dashboard-tabs-list .dashboard-tab-trigger.dashboard-tab-trigger-active,
|
|
25
25
|
.dashboard-tabs-list
|
|
26
26
|
.dashboard-tab-trigger.dashboard-tab-trigger-active[data-state='active'] {
|
|
27
27
|
z-index: 10;
|
|
28
|
-
background:
|
|
29
|
-
color:
|
|
28
|
+
background: var(--background) !important;
|
|
29
|
+
color: var(--primary) !important;
|
|
30
30
|
border: none !important;
|
|
31
31
|
box-shadow: none !important;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
.dashboard-tabs-list .dashboard-tab-trigger.dashboard-tab-trigger-active svg {
|
|
35
|
-
color:
|
|
35
|
+
color: var(--primary) !important;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
.dashboard-grid.react-grid-layout {
|
|
@@ -66,8 +66,8 @@
|
|
|
66
66
|
bottom: 3px;
|
|
67
67
|
width: 6px;
|
|
68
68
|
height: 6px;
|
|
69
|
-
border-right: 2px solid
|
|
70
|
-
border-bottom: 2px solid
|
|
69
|
+
border-right: 2px solid var(--muted-foreground);
|
|
70
|
+
border-bottom: 2px solid var(--muted-foreground);
|
|
71
71
|
opacity: 0.45;
|
|
72
72
|
}
|
|
73
73
|
|
|
@@ -125,16 +125,16 @@
|
|
|
125
125
|
|
|
126
126
|
/* Placeholder styling */
|
|
127
127
|
.dashboard-grid .react-grid-placeholder {
|
|
128
|
-
background:
|
|
128
|
+
background: color-mix(in oklab, var(--primary) 20%, transparent);
|
|
129
129
|
opacity: 0.5;
|
|
130
130
|
transition-duration: 100ms;
|
|
131
131
|
z-index: 2;
|
|
132
132
|
border-radius: 0.5rem;
|
|
133
|
-
border: 2px dashed
|
|
133
|
+
border: 2px dashed var(--primary);
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
.dashboard-grid .react-grid-item:hover {
|
|
137
|
-
outline: 1px solid
|
|
137
|
+
outline: 1px solid color-mix(in oklab, var(--primary) 12%, transparent);
|
|
138
138
|
outline-offset: -1px;
|
|
139
139
|
}
|
|
140
140
|
|
|
@@ -542,6 +542,48 @@ const PROVIDER_FIELDS: Record<string, FieldDef[]> = {
|
|
|
542
542
|
required: true,
|
|
543
543
|
},
|
|
544
544
|
],
|
|
545
|
+
'apple-oauth': [
|
|
546
|
+
{
|
|
547
|
+
key: 'client_id',
|
|
548
|
+
labelKey: 'fieldClientId',
|
|
549
|
+
type: 'text',
|
|
550
|
+
required: true,
|
|
551
|
+
placeholder: 'com.yourdomain.service',
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
key: 'team_id',
|
|
555
|
+
labelKey: 'fieldTeamId',
|
|
556
|
+
type: 'text',
|
|
557
|
+
required: true,
|
|
558
|
+
},
|
|
559
|
+
{
|
|
560
|
+
key: 'key_id',
|
|
561
|
+
labelKey: 'fieldKeyId',
|
|
562
|
+
type: 'text',
|
|
563
|
+
required: true,
|
|
564
|
+
},
|
|
565
|
+
{
|
|
566
|
+
key: 'private_key',
|
|
567
|
+
labelKey: 'fieldPrivateKey',
|
|
568
|
+
type: 'password',
|
|
569
|
+
required: true,
|
|
570
|
+
placeholder: '-----BEGIN PRIVATE KEY-----...',
|
|
571
|
+
},
|
|
572
|
+
],
|
|
573
|
+
'linkedin-oauth': [
|
|
574
|
+
{
|
|
575
|
+
key: 'client_id',
|
|
576
|
+
labelKey: 'fieldClientId',
|
|
577
|
+
type: 'text',
|
|
578
|
+
required: true,
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
key: 'client_secret',
|
|
582
|
+
labelKey: 'fieldClientSecret',
|
|
583
|
+
type: 'password',
|
|
584
|
+
required: true,
|
|
585
|
+
},
|
|
586
|
+
],
|
|
545
587
|
stripe: [
|
|
546
588
|
{
|
|
547
589
|
key: 'publishable_key',
|
|
@@ -992,7 +992,7 @@ function MenuTreeWorkspace({
|
|
|
992
992
|
'group relative mr-1 flex cursor-pointer items-center gap-2 rounded-xl border px-2 py-1.5 transition-all duration-150 select-none active:scale-[0.99]',
|
|
993
993
|
isDraggingThis ? 'opacity-40' : 'opacity-100',
|
|
994
994
|
selectedId === node.id
|
|
995
|
-
? 'border-primary/70 bg-linear-to-r from-primary/10 via-primary/5 to-background shadow-[0_10px_24px_-
|
|
995
|
+
? 'border-primary/70 bg-linear-to-r from-primary/10 via-primary/5 to-background shadow-[0_10px_24px_-18px_var(--primary)] ring-1 ring-primary/15'
|
|
996
996
|
: 'border-transparent hover:border-primary/30 hover:bg-muted/60 hover:shadow-sm',
|
|
997
997
|
isInside ? 'ring-2 ring-primary bg-primary/10' : ''
|
|
998
998
|
)}
|
|
@@ -147,6 +147,14 @@ const OAUTH_PROVIDER_TOGGLE_MAP: Array<{
|
|
|
147
147
|
name: 'Microsoft Entra ID',
|
|
148
148
|
},
|
|
149
149
|
},
|
|
150
|
+
{
|
|
151
|
+
settingSlug: 'oauth-apple-enabled',
|
|
152
|
+
provider: { id: 'apple', slug: 'apple-oauth', name: 'Apple' },
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
settingSlug: 'oauth-linkedin-enabled',
|
|
156
|
+
provider: { id: 'linkedin', slug: 'linkedin-oauth', name: 'LinkedIn' },
|
|
157
|
+
},
|
|
150
158
|
];
|
|
151
159
|
|
|
152
160
|
function generateRandomPassword(length = 16) {
|
|
@@ -870,6 +870,38 @@
|
|
|
870
870
|
"loading": "Loading...",
|
|
871
871
|
"errorOccurred": "An error occurred.",
|
|
872
872
|
"alreadyProcessed": "This authentication callback has already been processed. Start the flow again."
|
|
873
|
+
},
|
|
874
|
+
"apple": {
|
|
875
|
+
"loginTitle": "Completing login...",
|
|
876
|
+
"loginDescription": "Please wait while we process your Apple authentication",
|
|
877
|
+
"loginSuccess": "Login successful!",
|
|
878
|
+
"registerTitle": "Completing registration...",
|
|
879
|
+
"registerDescription": "Please wait while we process your Apple authentication",
|
|
880
|
+
"registerSuccess": "Account created successfully!",
|
|
881
|
+
"connectTitle": "Completing account connection...",
|
|
882
|
+
"connectDescription": "Please wait while we process your Apple authentication",
|
|
883
|
+
"connectSuccess": "Account connected successfully!",
|
|
884
|
+
"errorTitle": "Unable to authenticate",
|
|
885
|
+
"buttonRetry": "Try again",
|
|
886
|
+
"loading": "Loading...",
|
|
887
|
+
"errorOccurred": "An error occurred.",
|
|
888
|
+
"alreadyProcessed": "This authentication callback has already been processed. Start the flow again."
|
|
889
|
+
},
|
|
890
|
+
"linkedin": {
|
|
891
|
+
"loginTitle": "Completing login...",
|
|
892
|
+
"loginDescription": "Please wait while we process your LinkedIn authentication",
|
|
893
|
+
"loginSuccess": "Login successful!",
|
|
894
|
+
"registerTitle": "Completing registration...",
|
|
895
|
+
"registerDescription": "Please wait while we process your LinkedIn authentication",
|
|
896
|
+
"registerSuccess": "Account created successfully!",
|
|
897
|
+
"connectTitle": "Completing account connection...",
|
|
898
|
+
"connectDescription": "Please wait while we process your LinkedIn authentication",
|
|
899
|
+
"connectSuccess": "Account connected successfully!",
|
|
900
|
+
"errorTitle": "Unable to authenticate",
|
|
901
|
+
"buttonRetry": "Try again",
|
|
902
|
+
"loading": "Loading...",
|
|
903
|
+
"errorOccurred": "An error occurred.",
|
|
904
|
+
"alreadyProcessed": "This authentication callback has already been processed. Start the flow again."
|
|
873
905
|
}
|
|
874
906
|
},
|
|
875
907
|
"ConnectedAccounts": {
|
|
@@ -2408,6 +2440,9 @@
|
|
|
2408
2440
|
"fieldOrganization": "Organization ID",
|
|
2409
2441
|
"fieldRedirectUri": "Redirect URI",
|
|
2410
2442
|
"fieldTenantId": "Tenant ID",
|
|
2443
|
+
"fieldTeamId": "Team ID",
|
|
2444
|
+
"fieldKeyId": "Key ID",
|
|
2445
|
+
"fieldPrivateKey": "Private Key (.p8)",
|
|
2411
2446
|
"fieldPublishableKey": "Publishable Key",
|
|
2412
2447
|
"fieldSecretKey": "Secret Key",
|
|
2413
2448
|
"fieldWebhookSecret": "Webhook Secret",
|
|
@@ -870,6 +870,38 @@
|
|
|
870
870
|
"loading": "Carregando...",
|
|
871
871
|
"errorOccurred": "Ocorreu um erro.",
|
|
872
872
|
"alreadyProcessed": "Esta autenticacao ja foi processada. Inicie o fluxo novamente."
|
|
873
|
+
},
|
|
874
|
+
"apple": {
|
|
875
|
+
"loginTitle": "Finalizando login...",
|
|
876
|
+
"loginDescription": "Aguarde enquanto processamos sua autenticação com a Apple",
|
|
877
|
+
"loginSuccess": "Login realizado com sucesso!",
|
|
878
|
+
"registerTitle": "Finalizando cadastro...",
|
|
879
|
+
"registerDescription": "Aguarde enquanto processamos sua autenticação com a Apple",
|
|
880
|
+
"registerSuccess": "Conta criada com sucesso!",
|
|
881
|
+
"connectTitle": "Finalizando conexão da conta...",
|
|
882
|
+
"connectDescription": "Aguarde enquanto processamos sua autenticação com a Apple",
|
|
883
|
+
"connectSuccess": "Conta conectada com sucesso!",
|
|
884
|
+
"errorTitle": "Não foi possível autenticar",
|
|
885
|
+
"buttonRetry": "Tentar novamente",
|
|
886
|
+
"loading": "Carregando...",
|
|
887
|
+
"errorOccurred": "Ocorreu um erro.",
|
|
888
|
+
"alreadyProcessed": "Esta autenticacao ja foi processada. Inicie o fluxo novamente."
|
|
889
|
+
},
|
|
890
|
+
"linkedin": {
|
|
891
|
+
"loginTitle": "Finalizando login...",
|
|
892
|
+
"loginDescription": "Aguarde enquanto processamos sua autenticação com o LinkedIn",
|
|
893
|
+
"loginSuccess": "Login realizado com sucesso!",
|
|
894
|
+
"registerTitle": "Finalizando cadastro...",
|
|
895
|
+
"registerDescription": "Aguarde enquanto processamos sua autenticação com o LinkedIn",
|
|
896
|
+
"registerSuccess": "Conta criada com sucesso!",
|
|
897
|
+
"connectTitle": "Finalizando conexão da conta...",
|
|
898
|
+
"connectDescription": "Aguarde enquanto processamos sua autenticação com o LinkedIn",
|
|
899
|
+
"connectSuccess": "Conta conectada com sucesso!",
|
|
900
|
+
"errorTitle": "Não foi possível autenticar",
|
|
901
|
+
"buttonRetry": "Tentar novamente",
|
|
902
|
+
"loading": "Carregando...",
|
|
903
|
+
"errorOccurred": "Ocorreu um erro.",
|
|
904
|
+
"alreadyProcessed": "Esta autenticacao ja foi processada. Inicie o fluxo novamente."
|
|
873
905
|
}
|
|
874
906
|
},
|
|
875
907
|
"ConnectedAccounts": {
|
|
@@ -2463,6 +2495,9 @@
|
|
|
2463
2495
|
"fieldOrganization": "ID da Organização",
|
|
2464
2496
|
"fieldRedirectUri": "URI de Redirecionamento",
|
|
2465
2497
|
"fieldTenantId": "Tenant ID",
|
|
2498
|
+
"fieldTeamId": "Team ID",
|
|
2499
|
+
"fieldKeyId": "Key ID",
|
|
2500
|
+
"fieldPrivateKey": "Chave Privada (.p8)",
|
|
2466
2501
|
"fieldPublishableKey": "Chave Publicável",
|
|
2467
2502
|
"fieldSecretKey": "Chave Secreta",
|
|
2468
2503
|
"fieldWebhookSecret": "Segredo do Webhook",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hed-hog/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.374",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -41,10 +41,10 @@
|
|
|
41
41
|
"speakeasy": "^2.0.0",
|
|
42
42
|
"ua-parser-js": "^2.0.10",
|
|
43
43
|
"uuid": "^11.1.0",
|
|
44
|
-
"@hed-hog/api": "0.0.8",
|
|
45
44
|
"@hed-hog/api-locale": "0.0.14",
|
|
46
|
-
"@hed-hog/api-mail": "0.0.9",
|
|
47
45
|
"@hed-hog/api-pagination": "0.0.7",
|
|
46
|
+
"@hed-hog/api": "0.0.8",
|
|
47
|
+
"@hed-hog/api-mail": "0.0.9",
|
|
48
48
|
"@hed-hog/api-types": "0.0.1",
|
|
49
49
|
"@hed-hog/api-prisma": "0.0.6"
|
|
50
50
|
},
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { describe, it, expect, jest, beforeEach, afterEach } from '@jest/globals';
|
|
2
|
+
|
|
3
|
+
jest.mock('@hed-hog/api-prisma', () => ({
|
|
4
|
+
__esModule: true,
|
|
5
|
+
PrismaService: class {},
|
|
6
|
+
Prisma: {
|
|
7
|
+
sql: (...args: any[]) => ({ __sql: args }),
|
|
8
|
+
raw: (v: string) => ({ __raw: v }),
|
|
9
|
+
},
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
jest.mock('@hed-hog/api-pagination', () => ({
|
|
13
|
+
__esModule: true,
|
|
14
|
+
PaginationService: class {},
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
jest.mock('geoip-lite', () => ({
|
|
18
|
+
__esModule: true,
|
|
19
|
+
lookup: jest.fn(),
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
jest.mock('ua-parser-js', () => ({
|
|
23
|
+
__esModule: true,
|
|
24
|
+
UAParser: jest.fn(),
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
import * as geoip from 'geoip-lite';
|
|
28
|
+
import { UAParser } from 'ua-parser-js';
|
|
29
|
+
import { AccessLogService } from './access-log.service';
|
|
30
|
+
|
|
31
|
+
const mockLookup = geoip.lookup as unknown as jest.Mock;
|
|
32
|
+
const mockUAParser = UAParser as unknown as jest.Mock;
|
|
33
|
+
|
|
34
|
+
const makeService = () => {
|
|
35
|
+
const prisma = {
|
|
36
|
+
access_log: { create: jest.fn() },
|
|
37
|
+
$executeRaw: jest.fn(),
|
|
38
|
+
};
|
|
39
|
+
const pagination = {
|
|
40
|
+
paginate: jest.fn(),
|
|
41
|
+
};
|
|
42
|
+
const service = new AccessLogService(prisma as any, pagination as any);
|
|
43
|
+
return { service, prisma, pagination };
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
describe('AccessLogService', () => {
|
|
47
|
+
beforeEach(() => {
|
|
48
|
+
mockUAParser.mockImplementation(() => ({
|
|
49
|
+
getResult: () => ({
|
|
50
|
+
browser: { name: 'Chrome', version: '120' },
|
|
51
|
+
os: { name: 'Windows', version: '11' },
|
|
52
|
+
}),
|
|
53
|
+
}));
|
|
54
|
+
mockLookup.mockReturnValue({ country: 'BR', city: 'Sao Paulo' });
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
afterEach(() => {
|
|
58
|
+
jest.clearAllMocks();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('recordHttp', () => {
|
|
62
|
+
it('persiste o log HTTP com os campos mapeados', () => {
|
|
63
|
+
const { service, prisma } = makeService();
|
|
64
|
+
prisma.access_log.create.mockResolvedValue({});
|
|
65
|
+
|
|
66
|
+
service.recordHttp({
|
|
67
|
+
userId: 3,
|
|
68
|
+
ip: '1.2.3.4',
|
|
69
|
+
userAgent: 'UA',
|
|
70
|
+
method: 'GET',
|
|
71
|
+
path: '/api/x',
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
expect(prisma.access_log.create).toHaveBeenCalledWith({
|
|
75
|
+
data: {
|
|
76
|
+
user_id: 3,
|
|
77
|
+
ip: '1.2.3.4',
|
|
78
|
+
user_agent: 'UA',
|
|
79
|
+
method: 'GET',
|
|
80
|
+
path: '/api/x',
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('usa null para campos opcionais ausentes', () => {
|
|
86
|
+
const { service, prisma } = makeService();
|
|
87
|
+
prisma.access_log.create.mockResolvedValue({});
|
|
88
|
+
|
|
89
|
+
service.recordHttp({ method: 'POST', path: '/x' });
|
|
90
|
+
|
|
91
|
+
expect(prisma.access_log.create).toHaveBeenCalledWith({
|
|
92
|
+
data: { user_id: null, ip: null, user_agent: null, method: 'POST', path: '/x' },
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('não propaga erro de persistência (loga warn)', async () => {
|
|
97
|
+
const { service, prisma } = makeService();
|
|
98
|
+
prisma.access_log.create.mockRejectedValue(new Error('db down'));
|
|
99
|
+
const warnSpy = jest
|
|
100
|
+
.spyOn((service as any).logger, 'warn')
|
|
101
|
+
.mockImplementation(() => undefined);
|
|
102
|
+
|
|
103
|
+
expect(() => service.recordHttp({ method: 'GET', path: '/x' })).not.toThrow();
|
|
104
|
+
await new Promise((r) => process.nextTick(r));
|
|
105
|
+
|
|
106
|
+
expect(warnSpy).toHaveBeenCalled();
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe('recordMcp', () => {
|
|
111
|
+
it('persiste o log MCP com tool e prompt', () => {
|
|
112
|
+
const { service, prisma } = makeService();
|
|
113
|
+
prisma.access_log.create.mockResolvedValue({});
|
|
114
|
+
|
|
115
|
+
service.recordMcp({ userId: 9, mcpTool: 'search', mcpPrompt: 'q' });
|
|
116
|
+
|
|
117
|
+
expect(prisma.access_log.create).toHaveBeenCalledWith({
|
|
118
|
+
data: { user_id: 9, mcp_tool: 'search', mcp_prompt: 'q' },
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
describe('list', () => {
|
|
124
|
+
const paginateResult = (data: any[]) => ({ data, total: data.length, page: 1 });
|
|
125
|
+
|
|
126
|
+
it('sem filtros usa where vazio e mantém orderBy/include', async () => {
|
|
127
|
+
const { service, pagination } = makeService();
|
|
128
|
+
pagination.paginate.mockResolvedValue(paginateResult([]));
|
|
129
|
+
|
|
130
|
+
await service.list({});
|
|
131
|
+
|
|
132
|
+
const [, , opts] = pagination.paginate.mock.calls[0];
|
|
133
|
+
expect(opts.where).toEqual({});
|
|
134
|
+
expect(opts.orderBy).toEqual({ created_at: 'desc' });
|
|
135
|
+
expect(opts.include.user.select).toMatchObject({ id: true, name: true });
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('filtra por userId', async () => {
|
|
139
|
+
const { service, pagination } = makeService();
|
|
140
|
+
pagination.paginate.mockResolvedValue(paginateResult([]));
|
|
141
|
+
|
|
142
|
+
await service.list({ userId: 5 });
|
|
143
|
+
|
|
144
|
+
expect(pagination.paginate.mock.calls[0][2].where).toMatchObject({ user_id: 5 });
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('filtra por userSearch com contains insensitive', async () => {
|
|
148
|
+
const { service, pagination } = makeService();
|
|
149
|
+
pagination.paginate.mockResolvedValue(paginateResult([]));
|
|
150
|
+
|
|
151
|
+
await service.list({ userSearch: 'ana' });
|
|
152
|
+
|
|
153
|
+
expect(pagination.paginate.mock.calls[0][2].where.user).toEqual({
|
|
154
|
+
OR: [{ name: { contains: 'ana', mode: 'insensitive' } }],
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('monta o range de datas com fim de dia em createdAtTo', async () => {
|
|
159
|
+
const { service, pagination } = makeService();
|
|
160
|
+
pagination.paginate.mockResolvedValue(paginateResult([]));
|
|
161
|
+
|
|
162
|
+
await service.list({ createdAtFrom: '2026-01-01', createdAtTo: '2026-01-31' });
|
|
163
|
+
|
|
164
|
+
const where = pagination.paginate.mock.calls[0][2].where;
|
|
165
|
+
expect(where.created_at.gte).toEqual(new Date('2026-01-01'));
|
|
166
|
+
const lte: Date = where.created_at.lte;
|
|
167
|
+
expect(lte.getHours()).toBe(23);
|
|
168
|
+
expect(lte.getMinutes()).toBe(59);
|
|
169
|
+
expect(lte.getSeconds()).toBe(59);
|
|
170
|
+
expect(lte.getMilliseconds()).toBe(999);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('type=http filtra method not null; type=mcp filtra mcp_tool not null', async () => {
|
|
174
|
+
const { service, pagination } = makeService();
|
|
175
|
+
pagination.paginate.mockResolvedValue(paginateResult([]));
|
|
176
|
+
|
|
177
|
+
await service.list({ type: 'http' });
|
|
178
|
+
expect(pagination.paginate.mock.calls[0][2].where).toMatchObject({
|
|
179
|
+
method: { not: null },
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
await service.list({ type: 'mcp' });
|
|
183
|
+
expect(pagination.paginate.mock.calls[1][2].where).toMatchObject({
|
|
184
|
+
mcp_tool: { not: null },
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('enriquece cada log com dados de user-agent e geoip', async () => {
|
|
189
|
+
const { service, pagination } = makeService();
|
|
190
|
+
pagination.paginate.mockResolvedValue(
|
|
191
|
+
paginateResult([{ id: 1, user_agent: 'UA', ip: '8.8.8.8' }]),
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
const result = await service.list({});
|
|
195
|
+
|
|
196
|
+
expect(result.data[0]).toMatchObject({
|
|
197
|
+
browser_name: 'Chrome',
|
|
198
|
+
browser_version: '120',
|
|
199
|
+
os_name: 'Windows',
|
|
200
|
+
os_version: '11',
|
|
201
|
+
country: 'BR',
|
|
202
|
+
city: 'Sao Paulo',
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('usa null quando não há user_agent nem ip', async () => {
|
|
207
|
+
const { service, pagination } = makeService();
|
|
208
|
+
pagination.paginate.mockResolvedValue(
|
|
209
|
+
paginateResult([{ id: 2, user_agent: null, ip: null }]),
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
const result = await service.list({});
|
|
213
|
+
|
|
214
|
+
expect(result.data[0]).toMatchObject({
|
|
215
|
+
browser_name: null,
|
|
216
|
+
os_name: null,
|
|
217
|
+
country: null,
|
|
218
|
+
city: null,
|
|
219
|
+
});
|
|
220
|
+
expect(mockUAParser).not.toHaveBeenCalled();
|
|
221
|
+
expect(mockLookup).not.toHaveBeenCalled();
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
describe('clearOld', () => {
|
|
226
|
+
it('executa o DELETE em lote via $executeRaw', async () => {
|
|
227
|
+
const { service, prisma } = makeService();
|
|
228
|
+
prisma.$executeRaw.mockResolvedValue(1);
|
|
229
|
+
|
|
230
|
+
await service.clearOld(30, 500);
|
|
231
|
+
|
|
232
|
+
expect(prisma.$executeRaw).toHaveBeenCalledTimes(1);
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
});
|