Compare commits
No commits in common. "168010f05e9918eec1e47f49148b7070a93e2ed8" and "90249208b856b86581aaa0caa05167d57513e0ec" have entirely different histories.
168010f05e
...
90249208b8
|
|
@ -43,71 +43,6 @@
|
||||||
"globalStatus": "active",
|
"globalStatus": "active",
|
||||||
"createdAt": "2026-05-04T00:00:00.000Z",
|
"createdAt": "2026-05-04T00:00:00.000Z",
|
||||||
"updatedAt": "2026-05-04T15:26:08.500Z"
|
"updatedAt": "2026-05-04T15:26:08.500Z"
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "user_constr_dc_yahoo_com",
|
|
||||||
"authentikUserId": "25686308-f513-428d-b5fc-771b73414a57",
|
|
||||||
"name": "DC CONSTR",
|
|
||||||
"email": "constr_dc@yahoo.com",
|
|
||||||
"phone": null,
|
|
||||||
"position": null,
|
|
||||||
"notes": "Создан через публичную регистрацию по инвайту клиента DCTOUCH.",
|
|
||||||
"avatarUrl": "/storage/uploads/1777992885416-502c0a5d-94-944112_unicorn-clipart-mystical-unicorn-web-server.png",
|
|
||||||
"globalStatus": "active",
|
|
||||||
"createdAt": "2026-05-05T14:53:26.607Z",
|
|
||||||
"updatedAt": "2026-05-05T14:57:13.515Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "user_support_dctouch_ru",
|
|
||||||
"authentikUserId": "e3896e96-b9b4-49f7-b591-47b72ca25dc8",
|
|
||||||
"name": "DC SUPPORT",
|
|
||||||
"email": "support@dctouch.ru",
|
|
||||||
"phone": null,
|
|
||||||
"position": null,
|
|
||||||
"notes": "Создан через публичную регистрацию по инвайту клиента DCTOUCH.",
|
|
||||||
"avatarUrl": null,
|
|
||||||
"globalStatus": "active",
|
|
||||||
"createdAt": "2026-05-05T16:02:43.235Z",
|
|
||||||
"updatedAt": "2026-05-05T16:04:53.004Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "user_silverpsih007_gmail_com",
|
|
||||||
"authentikUserId": "43b3f644-2a99-4e3e-948f-f199660cc08e",
|
|
||||||
"name": "DC SILVER007",
|
|
||||||
"email": "silverpsih007@gmail.com",
|
|
||||||
"phone": null,
|
|
||||||
"position": null,
|
|
||||||
"notes": "Создан через публичную регистрацию по инвайту клиента DCTOUCH.",
|
|
||||||
"avatarUrl": null,
|
|
||||||
"globalStatus": "active",
|
|
||||||
"createdAt": "2026-05-05T17:26:50.184Z",
|
|
||||||
"updatedAt": "2026-05-05T17:27:40.754Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "user_abramov_dcconstructions_ru",
|
|
||||||
"authentikUserId": "58f6fb15-198d-4f6a-b93c-6e08f80deae1",
|
|
||||||
"name": "DC ABRAMOV",
|
|
||||||
"email": "abramov@dcconstructions.ru",
|
|
||||||
"phone": null,
|
|
||||||
"position": null,
|
|
||||||
"notes": "Создан через публичную регистрацию по инвайту клиента DCTOUCH.",
|
|
||||||
"avatarUrl": null,
|
|
||||||
"globalStatus": "active",
|
|
||||||
"createdAt": "2026-05-05T22:43:07.620Z",
|
|
||||||
"updatedAt": "2026-05-05T22:43:22.734Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "user_support_dcconstructions_ru",
|
|
||||||
"authentikUserId": "73e3c792-be23-4833-9355-fbfc38310f56",
|
|
||||||
"name": "DC CONSTRICTIONS",
|
|
||||||
"email": "support@dcconstructions.ru",
|
|
||||||
"phone": null,
|
|
||||||
"position": null,
|
|
||||||
"notes": "Создан через публичную регистрацию по инвайту клиента DCTOUCH.",
|
|
||||||
"avatarUrl": null,
|
|
||||||
"globalStatus": "active",
|
|
||||||
"createdAt": "2026-05-06T01:06:48.113Z",
|
|
||||||
"updatedAt": "2026-05-06T01:28:06.887Z"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"memberships": [
|
"memberships": [
|
||||||
|
|
@ -128,51 +63,6 @@
|
||||||
"status": "active",
|
"status": "active",
|
||||||
"createdAt": "2026-05-04T00:00:00.000Z",
|
"createdAt": "2026-05-04T00:00:00.000Z",
|
||||||
"updatedAt": "2026-05-04T12:55:13.842Z"
|
"updatedAt": "2026-05-04T12:55:13.842Z"
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "mem_client_romashka_constr_dc_yahoo_com",
|
|
||||||
"clientId": "client_romashka",
|
|
||||||
"userId": "user_constr_dc_yahoo_com",
|
|
||||||
"role": "member",
|
|
||||||
"status": "active",
|
|
||||||
"createdAt": "2026-05-05T14:53:26.607Z",
|
|
||||||
"updatedAt": "2026-05-05T14:53:26.607Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "mem_client_romashka_support_dctouch_ru",
|
|
||||||
"clientId": "client_romashka",
|
|
||||||
"userId": "user_support_dctouch_ru",
|
|
||||||
"role": "member",
|
|
||||||
"status": "active",
|
|
||||||
"createdAt": "2026-05-05T16:02:43.235Z",
|
|
||||||
"updatedAt": "2026-05-05T16:02:43.235Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "mem_client_romashka_silverpsih007_gmail_com",
|
|
||||||
"clientId": "client_romashka",
|
|
||||||
"userId": "user_silverpsih007_gmail_com",
|
|
||||||
"role": "member",
|
|
||||||
"status": "active",
|
|
||||||
"createdAt": "2026-05-05T17:26:50.184Z",
|
|
||||||
"updatedAt": "2026-05-05T17:26:50.184Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "mem_client_romashka_abramov_dcconstructions_ru",
|
|
||||||
"clientId": "client_romashka",
|
|
||||||
"userId": "user_abramov_dcconstructions_ru",
|
|
||||||
"role": "member",
|
|
||||||
"status": "active",
|
|
||||||
"createdAt": "2026-05-05T22:43:07.620Z",
|
|
||||||
"updatedAt": "2026-05-05T22:43:07.620Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "mem_client_romashka_support_dcconstructions_ru",
|
|
||||||
"clientId": "client_romashka",
|
|
||||||
"userId": "user_support_dcconstructions_ru",
|
|
||||||
"role": "member",
|
|
||||||
"status": "active",
|
|
||||||
"createdAt": "2026-05-06T01:06:48.113Z",
|
|
||||||
"updatedAt": "2026-05-06T01:06:48.113Z"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"groups": [
|
"groups": [
|
||||||
|
|
@ -369,121 +259,10 @@
|
||||||
"status": "active",
|
"status": "active",
|
||||||
"createdAt": "2026-05-04T15:26:07.830Z",
|
"createdAt": "2026-05-04T15:26:07.830Z",
|
||||||
"updatedAt": "2026-05-04T15:26:07.830Z"
|
"updatedAt": "2026-05-04T15:26:07.830Z"
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "grant_task_manager_user_constr_dc_yahoo_com",
|
|
||||||
"serviceId": "service_task_manager",
|
|
||||||
"targetType": "user",
|
|
||||||
"targetId": "user_constr_dc_yahoo_com",
|
|
||||||
"appRole": "member",
|
|
||||||
"status": "active",
|
|
||||||
"createdAt": "2026-05-05T14:57:13.249Z",
|
|
||||||
"updatedAt": "2026-05-05T14:57:13.249Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "grant_task_manager_user_support_dctouch_ru",
|
|
||||||
"serviceId": "service_task_manager",
|
|
||||||
"targetType": "user",
|
|
||||||
"targetId": "user_support_dctouch_ru",
|
|
||||||
"appRole": "member",
|
|
||||||
"status": "active",
|
|
||||||
"createdAt": "2026-05-05T16:04:52.709Z",
|
|
||||||
"updatedAt": "2026-05-05T16:04:52.709Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "grant_task_manager_user_silverpsih007_gmail_com",
|
|
||||||
"serviceId": "service_task_manager",
|
|
||||||
"targetType": "user",
|
|
||||||
"targetId": "user_silverpsih007_gmail_com",
|
|
||||||
"appRole": "member",
|
|
||||||
"status": "active",
|
|
||||||
"createdAt": "2026-05-05T17:27:40.540Z",
|
|
||||||
"updatedAt": "2026-05-05T17:27:40.540Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "grant_task_manager_user_abramov_dcconstructions_ru",
|
|
||||||
"serviceId": "service_task_manager",
|
|
||||||
"targetType": "user",
|
|
||||||
"targetId": "user_abramov_dcconstructions_ru",
|
|
||||||
"appRole": "member",
|
|
||||||
"status": "active",
|
|
||||||
"createdAt": "2026-05-05T22:43:22.308Z",
|
|
||||||
"updatedAt": "2026-05-05T22:43:22.308Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "grant_task_manager_user_support_dcconstructions_ru",
|
|
||||||
"serviceId": "service_task_manager",
|
|
||||||
"targetType": "user",
|
|
||||||
"targetId": "user_support_dcconstructions_ru",
|
|
||||||
"appRole": "member",
|
|
||||||
"status": "active",
|
|
||||||
"createdAt": "2026-05-06T01:28:06.515Z",
|
|
||||||
"updatedAt": "2026-05-06T01:28:06.515Z"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"exceptions": [],
|
"exceptions": [],
|
||||||
"invites": [
|
"invites": [],
|
||||||
{
|
|
||||||
"id": "invite_constr_dc_yahoo_com",
|
|
||||||
"clientId": "client_romashka",
|
|
||||||
"email": "constr_dc@yahoo.com",
|
|
||||||
"role": "member",
|
|
||||||
"invitedByUserId": "user_root",
|
|
||||||
"token": "856d7ef7-f0e0-4f53-8fac-27ed89e67ea0",
|
|
||||||
"expiresAt": "2026-05-12T13:38:27.256Z",
|
|
||||||
"status": "accepted",
|
|
||||||
"createdAt": "2026-05-05T13:38:27.256Z",
|
|
||||||
"updatedAt": "2026-05-05T14:53:26.607Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "invite_support_dctouch_ru",
|
|
||||||
"clientId": "client_romashka",
|
|
||||||
"email": "support@dctouch.ru",
|
|
||||||
"role": "member",
|
|
||||||
"invitedByUserId": "user_root",
|
|
||||||
"token": "2b8c2c1f-384d-4514-b566-b788b771fe7b",
|
|
||||||
"expiresAt": "2026-05-12T16:01:01.781Z",
|
|
||||||
"status": "accepted",
|
|
||||||
"createdAt": "2026-05-05T16:01:01.781Z",
|
|
||||||
"updatedAt": "2026-05-05T16:02:43.235Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "invite_silverpsih007_gmail_com",
|
|
||||||
"clientId": "client_romashka",
|
|
||||||
"email": "silverpsih007@gmail.com",
|
|
||||||
"role": "member",
|
|
||||||
"invitedByUserId": "user_root",
|
|
||||||
"token": "fed64a57-bcf5-4e12-b6c9-dbb48725cad6",
|
|
||||||
"expiresAt": "2026-05-12T17:25:29.607Z",
|
|
||||||
"status": "accepted",
|
|
||||||
"createdAt": "2026-05-05T17:25:29.607Z",
|
|
||||||
"updatedAt": "2026-05-05T17:26:50.184Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "invite_abramov_dcconstructions_ru",
|
|
||||||
"clientId": "client_romashka",
|
|
||||||
"email": "abramov@dcconstructions.ru",
|
|
||||||
"role": "member",
|
|
||||||
"invitedByUserId": "user_root",
|
|
||||||
"token": "fca2b781-8e42-4c14-beac-3f67a58e28bd",
|
|
||||||
"expiresAt": "2026-05-12T22:41:52.296Z",
|
|
||||||
"status": "accepted",
|
|
||||||
"createdAt": "2026-05-05T22:41:52.296Z",
|
|
||||||
"updatedAt": "2026-05-05T22:43:07.620Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "invite_support_dcconstructions_ru",
|
|
||||||
"clientId": "client_romashka",
|
|
||||||
"email": "support@dcconstructions.ru",
|
|
||||||
"role": "member",
|
|
||||||
"invitedByUserId": "user_root",
|
|
||||||
"token": "3f5293a4-ed05-455b-ae65-1c91d9496519",
|
|
||||||
"expiresAt": "2026-05-13T01:04:54.007Z",
|
|
||||||
"status": "accepted",
|
|
||||||
"createdAt": "2026-05-06T01:04:54.007Z",
|
|
||||||
"updatedAt": "2026-05-06T01:06:48.113Z"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"syncStatuses": [
|
"syncStatuses": [
|
||||||
{
|
{
|
||||||
"id": "sync_dctouch_client_authentik",
|
"id": "sync_dctouch_client_authentik",
|
||||||
|
|
@ -561,171 +340,6 @@
|
||||||
"lastSyncAt": null,
|
"lastSyncAt": null,
|
||||||
"error": null,
|
"error": null,
|
||||||
"updatedAt": "2026-05-04T15:26:07.830Z"
|
"updatedAt": "2026-05-04T15:26:07.830Z"
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "sync_invite_invite_constr_dc_yahoo_com",
|
|
||||||
"objectId": "invite_constr_dc_yahoo_com",
|
|
||||||
"objectName": "constr_dc@yahoo.com",
|
|
||||||
"objectType": "invite",
|
|
||||||
"target": "authentik",
|
|
||||||
"state": "pending",
|
|
||||||
"lastSyncAt": null,
|
|
||||||
"error": null,
|
|
||||||
"updatedAt": "2026-05-05T13:38:28.642Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "sync_user_user_constr_dc_yahoo_com",
|
|
||||||
"objectId": "user_constr_dc_yahoo_com",
|
|
||||||
"objectName": "constr_dc@yahoo.com",
|
|
||||||
"objectType": "user",
|
|
||||||
"target": "authentik",
|
|
||||||
"state": "synced",
|
|
||||||
"lastSyncAt": "2026-05-05T14:57:13.515Z",
|
|
||||||
"error": null,
|
|
||||||
"updatedAt": "2026-05-05T14:57:13.515Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "sync_grant_service_task_manager_user_constr_dc_yahoo_com",
|
|
||||||
"objectId": "service_task_manager:user_constr_dc_yahoo_com",
|
|
||||||
"objectName": "task-manager:constr_dc@yahoo.com",
|
|
||||||
"objectType": "grant",
|
|
||||||
"target": "authentik",
|
|
||||||
"state": "pending",
|
|
||||||
"lastSyncAt": null,
|
|
||||||
"error": null,
|
|
||||||
"updatedAt": "2026-05-05T14:57:13.253Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "sync_invite_invite_support_dctouch_ru",
|
|
||||||
"objectId": "invite_support_dctouch_ru",
|
|
||||||
"objectName": "support@dctouch.ru",
|
|
||||||
"objectType": "invite",
|
|
||||||
"target": "authentik",
|
|
||||||
"state": "pending",
|
|
||||||
"lastSyncAt": null,
|
|
||||||
"error": null,
|
|
||||||
"updatedAt": "2026-05-05T16:01:06.477Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "sync_user_user_support_dctouch_ru",
|
|
||||||
"objectId": "user_support_dctouch_ru",
|
|
||||||
"objectName": "support@dctouch.ru",
|
|
||||||
"objectType": "user",
|
|
||||||
"target": "authentik",
|
|
||||||
"state": "synced",
|
|
||||||
"lastSyncAt": "2026-05-05T16:04:53.004Z",
|
|
||||||
"error": null,
|
|
||||||
"updatedAt": "2026-05-05T16:04:53.004Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "sync_grant_service_task_manager_user_support_dctouch_ru",
|
|
||||||
"objectId": "service_task_manager:user_support_dctouch_ru",
|
|
||||||
"objectName": "task-manager:support@dctouch.ru",
|
|
||||||
"objectType": "grant",
|
|
||||||
"target": "authentik",
|
|
||||||
"state": "pending",
|
|
||||||
"lastSyncAt": null,
|
|
||||||
"error": null,
|
|
||||||
"updatedAt": "2026-05-05T16:04:52.709Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "sync_invite_invite_silverpsih007_gmail_com",
|
|
||||||
"objectId": "invite_silverpsih007_gmail_com",
|
|
||||||
"objectName": "silverpsih007@gmail.com",
|
|
||||||
"objectType": "invite",
|
|
||||||
"target": "authentik",
|
|
||||||
"state": "pending",
|
|
||||||
"lastSyncAt": null,
|
|
||||||
"error": null,
|
|
||||||
"updatedAt": "2026-05-05T17:25:31.407Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "sync_user_user_silverpsih007_gmail_com",
|
|
||||||
"objectId": "user_silverpsih007_gmail_com",
|
|
||||||
"objectName": "silverpsih007@gmail.com",
|
|
||||||
"objectType": "user",
|
|
||||||
"target": "authentik",
|
|
||||||
"state": "synced",
|
|
||||||
"lastSyncAt": "2026-05-05T17:27:40.754Z",
|
|
||||||
"error": null,
|
|
||||||
"updatedAt": "2026-05-05T17:27:40.754Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "sync_grant_service_task_manager_user_silverpsih007_gmail_com",
|
|
||||||
"objectId": "service_task_manager:user_silverpsih007_gmail_com",
|
|
||||||
"objectName": "task-manager:silverpsih007@gmail.com",
|
|
||||||
"objectType": "grant",
|
|
||||||
"target": "authentik",
|
|
||||||
"state": "pending",
|
|
||||||
"lastSyncAt": null,
|
|
||||||
"error": null,
|
|
||||||
"updatedAt": "2026-05-05T17:27:40.543Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "sync_invite_invite_abramov_dcconstructions_ru",
|
|
||||||
"objectId": "invite_abramov_dcconstructions_ru",
|
|
||||||
"objectName": "abramov@dcconstructions.ru",
|
|
||||||
"objectType": "invite",
|
|
||||||
"target": "authentik",
|
|
||||||
"state": "pending",
|
|
||||||
"lastSyncAt": null,
|
|
||||||
"error": null,
|
|
||||||
"updatedAt": "2026-05-05T22:41:56.561Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "sync_user_user_abramov_dcconstructions_ru",
|
|
||||||
"objectId": "user_abramov_dcconstructions_ru",
|
|
||||||
"objectName": "abramov@dcconstructions.ru",
|
|
||||||
"objectType": "user",
|
|
||||||
"target": "authentik",
|
|
||||||
"state": "synced",
|
|
||||||
"lastSyncAt": "2026-05-05T22:43:22.734Z",
|
|
||||||
"error": null,
|
|
||||||
"updatedAt": "2026-05-05T22:43:22.734Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "sync_grant_service_task_manager_user_abramov_dcconstructions_ru",
|
|
||||||
"objectId": "service_task_manager:user_abramov_dcconstructions_ru",
|
|
||||||
"objectName": "task-manager:abramov@dcconstructions.ru",
|
|
||||||
"objectType": "grant",
|
|
||||||
"target": "authentik",
|
|
||||||
"state": "pending",
|
|
||||||
"lastSyncAt": null,
|
|
||||||
"error": null,
|
|
||||||
"updatedAt": "2026-05-05T22:43:22.308Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "sync_invite_invite_support_dcconstructions_ru",
|
|
||||||
"objectId": "invite_support_dcconstructions_ru",
|
|
||||||
"objectName": "support@dcconstructions.ru",
|
|
||||||
"objectType": "invite",
|
|
||||||
"target": "authentik",
|
|
||||||
"state": "pending",
|
|
||||||
"lastSyncAt": null,
|
|
||||||
"error": null,
|
|
||||||
"updatedAt": "2026-05-06T01:04:56.745Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "sync_user_user_support_dcconstructions_ru",
|
|
||||||
"objectId": "user_support_dcconstructions_ru",
|
|
||||||
"objectName": "support@dcconstructions.ru",
|
|
||||||
"objectType": "user",
|
|
||||||
"target": "authentik",
|
|
||||||
"state": "synced",
|
|
||||||
"lastSyncAt": "2026-05-06T01:28:06.887Z",
|
|
||||||
"error": null,
|
|
||||||
"updatedAt": "2026-05-06T01:28:06.887Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "sync_grant_service_task_manager_user_support_dcconstructions_ru",
|
|
||||||
"objectId": "service_task_manager:user_support_dcconstructions_ru",
|
|
||||||
"objectName": "task-manager:support@dcconstructions.ru",
|
|
||||||
"objectType": "grant",
|
|
||||||
"target": "authentik",
|
|
||||||
"state": "pending",
|
|
||||||
"lastSyncAt": null,
|
|
||||||
"error": null,
|
|
||||||
"updatedAt": "2026-05-06T01:28:06.516Z"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"auditEvents": [
|
"auditEvents": [
|
||||||
|
|
@ -1532,426 +1146,6 @@
|
||||||
"clientId": null,
|
"clientId": null,
|
||||||
"result": "success",
|
"result": "success",
|
||||||
"details": "Logo link: http://launcher.local.nodedc/"
|
"details": "Logo link: http://launcher.local.nodedc/"
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_constr_dc_yahoo_com",
|
|
||||||
"at": "2026-05-05T13:09:04.396Z",
|
|
||||||
"actorUserId": "user_root",
|
|
||||||
"actorName": "DC SUDO",
|
|
||||||
"action": "Создан инвайт",
|
|
||||||
"objectType": "invite",
|
|
||||||
"objectName": "constr_dc@yahoo.com",
|
|
||||||
"clientId": "client_romashka",
|
|
||||||
"result": "success",
|
|
||||||
"details": "Роль: member"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_constr_dc_yahoo_com_2",
|
|
||||||
"at": "2026-05-05T13:09:11.185Z",
|
|
||||||
"actorUserId": "user_root",
|
|
||||||
"actorName": "DC SUDO",
|
|
||||||
"action": "Обновлён инвайт",
|
|
||||||
"objectType": "invite",
|
|
||||||
"objectName": "constr_dc@yahoo.com",
|
|
||||||
"clientId": "client_romashka",
|
|
||||||
"result": "success",
|
|
||||||
"details": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_constr_dc_yahoo_com_3",
|
|
||||||
"at": "2026-05-05T13:38:19.890Z",
|
|
||||||
"actorUserId": "user_root",
|
|
||||||
"actorName": "DC SUDO",
|
|
||||||
"action": "Удалён инвайт",
|
|
||||||
"objectType": "invite",
|
|
||||||
"objectName": "constr_dc@yahoo.com",
|
|
||||||
"clientId": "client_romashka",
|
|
||||||
"result": "warning",
|
|
||||||
"details": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_constr_dc_yahoo_com_4",
|
|
||||||
"at": "2026-05-05T13:38:27.257Z",
|
|
||||||
"actorUserId": "user_root",
|
|
||||||
"actorName": "DC SUDO",
|
|
||||||
"action": "Создан инвайт",
|
|
||||||
"objectType": "invite",
|
|
||||||
"objectName": "constr_dc@yahoo.com",
|
|
||||||
"clientId": "client_romashka",
|
|
||||||
"result": "success",
|
|
||||||
"details": "Роль: member"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_constr_dc_yahoo_com_5",
|
|
||||||
"at": "2026-05-05T13:38:28.642Z",
|
|
||||||
"actorUserId": "user_root",
|
|
||||||
"actorName": "DC SUDO",
|
|
||||||
"action": "Обновлён инвайт",
|
|
||||||
"objectType": "invite",
|
|
||||||
"objectName": "constr_dc@yahoo.com",
|
|
||||||
"clientId": "client_romashka",
|
|
||||||
"result": "success",
|
|
||||||
"details": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_constr_dc_yahoo_com_6",
|
|
||||||
"at": "2026-05-05T14:53:26.608Z",
|
|
||||||
"actorUserId": "user_constr_dc_yahoo_com",
|
|
||||||
"actorName": "DC CONSTR",
|
|
||||||
"action": "Регистрация по инвайту",
|
|
||||||
"objectType": "invite",
|
|
||||||
"objectName": "constr_dc@yahoo.com",
|
|
||||||
"clientId": "client_romashka",
|
|
||||||
"result": "success",
|
|
||||||
"details": "Role: member"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_constr_dc_yahoo_com_7",
|
|
||||||
"at": "2026-05-05T14:53:26.613Z",
|
|
||||||
"actorUserId": "user_constr_dc_yahoo_com",
|
|
||||||
"actorName": "DC CONSTR",
|
|
||||||
"action": "Пользователь синхронизирован в Authentik",
|
|
||||||
"objectType": "user",
|
|
||||||
"objectName": "constr_dc@yahoo.com",
|
|
||||||
"clientId": null,
|
|
||||||
"result": "success",
|
|
||||||
"details": "Groups: nodedc:launcher:user"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_constr_dc_yahoo_com_8",
|
|
||||||
"at": "2026-05-05T14:54:46.980Z",
|
|
||||||
"actorUserId": "user_constr_dc_yahoo_com",
|
|
||||||
"actorName": "DC CONSTR",
|
|
||||||
"action": "Обновлён профиль пользователя",
|
|
||||||
"objectType": "user",
|
|
||||||
"objectName": "constr_dc@yahoo.com",
|
|
||||||
"clientId": null,
|
|
||||||
"result": "success",
|
|
||||||
"details": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_constr_dc_yahoo_com_9",
|
|
||||||
"at": "2026-05-05T14:54:47.242Z",
|
|
||||||
"actorUserId": "user_constr_dc_yahoo_com",
|
|
||||||
"actorName": "DC CONSTR",
|
|
||||||
"action": "Пользователь синхронизирован в Authentik",
|
|
||||||
"objectType": "user",
|
|
||||||
"objectName": "constr_dc@yahoo.com",
|
|
||||||
"clientId": null,
|
|
||||||
"result": "success",
|
|
||||||
"details": "Groups: nodedc:launcher:user"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_constr_dc_yahoo_com_task_manager",
|
|
||||||
"at": "2026-05-05T14:57:13.253Z",
|
|
||||||
"actorUserId": "user_root",
|
|
||||||
"actorName": "DC SUDO",
|
|
||||||
"action": "Обновлён доступ пользователя к сервису",
|
|
||||||
"objectType": "grant",
|
|
||||||
"objectName": "constr_dc@yahoo.com / task-manager",
|
|
||||||
"clientId": null,
|
|
||||||
"result": "success",
|
|
||||||
"details": "Value: member"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_constr_dc_yahoo_com_10",
|
|
||||||
"at": "2026-05-05T14:57:13.515Z",
|
|
||||||
"actorUserId": "user_root",
|
|
||||||
"actorName": "DC SUDO",
|
|
||||||
"action": "Пользователь синхронизирован в Authentik",
|
|
||||||
"objectType": "user",
|
|
||||||
"objectName": "constr_dc@yahoo.com",
|
|
||||||
"clientId": null,
|
|
||||||
"result": "success",
|
|
||||||
"details": "Groups: nodedc:launcher:user, nodedc:taskmanager:user"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_support_dctouch_ru",
|
|
||||||
"at": "2026-05-05T16:01:01.784Z",
|
|
||||||
"actorUserId": "user_root",
|
|
||||||
"actorName": "DC SUDO",
|
|
||||||
"action": "Создан инвайт",
|
|
||||||
"objectType": "invite",
|
|
||||||
"objectName": "support@dctouch.ru",
|
|
||||||
"clientId": "client_romashka",
|
|
||||||
"result": "success",
|
|
||||||
"details": "Роль: member"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_support_dctouch_ru_2",
|
|
||||||
"at": "2026-05-05T16:01:06.477Z",
|
|
||||||
"actorUserId": "user_root",
|
|
||||||
"actorName": "DC SUDO",
|
|
||||||
"action": "Обновлён инвайт",
|
|
||||||
"objectType": "invite",
|
|
||||||
"objectName": "support@dctouch.ru",
|
|
||||||
"clientId": "client_romashka",
|
|
||||||
"result": "success",
|
|
||||||
"details": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_support_dctouch_ru_3",
|
|
||||||
"at": "2026-05-05T16:02:43.235Z",
|
|
||||||
"actorUserId": "user_support_dctouch_ru",
|
|
||||||
"actorName": "DC SUPPORT",
|
|
||||||
"action": "Регистрация по инвайту",
|
|
||||||
"objectType": "invite",
|
|
||||||
"objectName": "support@dctouch.ru",
|
|
||||||
"clientId": "client_romashka",
|
|
||||||
"result": "success",
|
|
||||||
"details": "Role: member"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_support_dctouch_ru_4",
|
|
||||||
"at": "2026-05-05T16:02:43.242Z",
|
|
||||||
"actorUserId": "user_support_dctouch_ru",
|
|
||||||
"actorName": "DC SUPPORT",
|
|
||||||
"action": "Пользователь синхронизирован в Authentik",
|
|
||||||
"objectType": "user",
|
|
||||||
"objectName": "support@dctouch.ru",
|
|
||||||
"clientId": null,
|
|
||||||
"result": "success",
|
|
||||||
"details": "Groups: nodedc:launcher:user"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_support_dctouch_ru_task_manager",
|
|
||||||
"at": "2026-05-05T16:04:52.709Z",
|
|
||||||
"actorUserId": "user_root",
|
|
||||||
"actorName": "DC SUDO",
|
|
||||||
"action": "Обновлён доступ пользователя к сервису",
|
|
||||||
"objectType": "grant",
|
|
||||||
"objectName": "support@dctouch.ru / task-manager",
|
|
||||||
"clientId": null,
|
|
||||||
"result": "success",
|
|
||||||
"details": "Value: member"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_support_dctouch_ru_5",
|
|
||||||
"at": "2026-05-05T16:04:53.004Z",
|
|
||||||
"actorUserId": "user_root",
|
|
||||||
"actorName": "DC SUDO",
|
|
||||||
"action": "Пользователь синхронизирован в Authentik",
|
|
||||||
"objectType": "user",
|
|
||||||
"objectName": "support@dctouch.ru",
|
|
||||||
"clientId": null,
|
|
||||||
"result": "success",
|
|
||||||
"details": "Groups: nodedc:launcher:user, nodedc:taskmanager:user"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_silverpsih007_gmail_com",
|
|
||||||
"at": "2026-05-05T17:25:29.608Z",
|
|
||||||
"actorUserId": "user_root",
|
|
||||||
"actorName": "DC SUDO",
|
|
||||||
"action": "Создан инвайт",
|
|
||||||
"objectType": "invite",
|
|
||||||
"objectName": "silverpsih007@gmail.com",
|
|
||||||
"clientId": "client_romashka",
|
|
||||||
"result": "success",
|
|
||||||
"details": "Роль: member"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_silverpsih007_gmail_com_2",
|
|
||||||
"at": "2026-05-05T17:25:31.407Z",
|
|
||||||
"actorUserId": "user_root",
|
|
||||||
"actorName": "DC SUDO",
|
|
||||||
"action": "Обновлён инвайт",
|
|
||||||
"objectType": "invite",
|
|
||||||
"objectName": "silverpsih007@gmail.com",
|
|
||||||
"clientId": "client_romashka",
|
|
||||||
"result": "success",
|
|
||||||
"details": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_silverpsih007_gmail_com_3",
|
|
||||||
"at": "2026-05-05T17:26:50.184Z",
|
|
||||||
"actorUserId": "user_silverpsih007_gmail_com",
|
|
||||||
"actorName": "DC SILVER007",
|
|
||||||
"action": "Регистрация по инвайту",
|
|
||||||
"objectType": "invite",
|
|
||||||
"objectName": "silverpsih007@gmail.com",
|
|
||||||
"clientId": "client_romashka",
|
|
||||||
"result": "success",
|
|
||||||
"details": "Role: member"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_silverpsih007_gmail_com_4",
|
|
||||||
"at": "2026-05-05T17:26:50.188Z",
|
|
||||||
"actorUserId": "user_silverpsih007_gmail_com",
|
|
||||||
"actorName": "DC SILVER007",
|
|
||||||
"action": "Пользователь синхронизирован в Authentik",
|
|
||||||
"objectType": "user",
|
|
||||||
"objectName": "silverpsih007@gmail.com",
|
|
||||||
"clientId": null,
|
|
||||||
"result": "success",
|
|
||||||
"details": "Groups: nodedc:launcher:user"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_silverpsih007_gmail_com_task_manager",
|
|
||||||
"at": "2026-05-05T17:27:40.543Z",
|
|
||||||
"actorUserId": "user_root",
|
|
||||||
"actorName": "DC SUDO",
|
|
||||||
"action": "Обновлён доступ пользователя к сервису",
|
|
||||||
"objectType": "grant",
|
|
||||||
"objectName": "silverpsih007@gmail.com / task-manager",
|
|
||||||
"clientId": null,
|
|
||||||
"result": "success",
|
|
||||||
"details": "Value: member"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_silverpsih007_gmail_com_5",
|
|
||||||
"at": "2026-05-05T17:27:40.754Z",
|
|
||||||
"actorUserId": "user_root",
|
|
||||||
"actorName": "DC SUDO",
|
|
||||||
"action": "Пользователь синхронизирован в Authentik",
|
|
||||||
"objectType": "user",
|
|
||||||
"objectName": "silverpsih007@gmail.com",
|
|
||||||
"clientId": null,
|
|
||||||
"result": "success",
|
|
||||||
"details": "Groups: nodedc:launcher:user, nodedc:taskmanager:user"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_abramov_dcconstructions_ru",
|
|
||||||
"at": "2026-05-05T22:41:52.301Z",
|
|
||||||
"actorUserId": "user_root",
|
|
||||||
"actorName": "DC SUDO",
|
|
||||||
"action": "Создан инвайт",
|
|
||||||
"objectType": "invite",
|
|
||||||
"objectName": "abramov@dcconstructions.ru",
|
|
||||||
"clientId": "client_romashka",
|
|
||||||
"result": "success",
|
|
||||||
"details": "Роль: member"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_abramov_dcconstructions_ru_2",
|
|
||||||
"at": "2026-05-05T22:41:56.561Z",
|
|
||||||
"actorUserId": "user_root",
|
|
||||||
"actorName": "DC SUDO",
|
|
||||||
"action": "Обновлён инвайт",
|
|
||||||
"objectType": "invite",
|
|
||||||
"objectName": "abramov@dcconstructions.ru",
|
|
||||||
"clientId": "client_romashka",
|
|
||||||
"result": "success",
|
|
||||||
"details": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_abramov_dcconstructions_ru_3",
|
|
||||||
"at": "2026-05-05T22:43:07.620Z",
|
|
||||||
"actorUserId": "user_abramov_dcconstructions_ru",
|
|
||||||
"actorName": "DC ABRAMOV",
|
|
||||||
"action": "Регистрация по инвайту",
|
|
||||||
"objectType": "invite",
|
|
||||||
"objectName": "abramov@dcconstructions.ru",
|
|
||||||
"clientId": "client_romashka",
|
|
||||||
"result": "success",
|
|
||||||
"details": "Role: member"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_abramov_dcconstructions_ru_4",
|
|
||||||
"at": "2026-05-05T22:43:07.624Z",
|
|
||||||
"actorUserId": "user_abramov_dcconstructions_ru",
|
|
||||||
"actorName": "DC ABRAMOV",
|
|
||||||
"action": "Пользователь синхронизирован в Authentik",
|
|
||||||
"objectType": "user",
|
|
||||||
"objectName": "abramov@dcconstructions.ru",
|
|
||||||
"clientId": null,
|
|
||||||
"result": "success",
|
|
||||||
"details": "Groups: nodedc:launcher:user"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_abramov_dcconstructions_ru_task_manager",
|
|
||||||
"at": "2026-05-05T22:43:22.308Z",
|
|
||||||
"actorUserId": "user_root",
|
|
||||||
"actorName": "DC SUDO",
|
|
||||||
"action": "Обновлён доступ пользователя к сервису",
|
|
||||||
"objectType": "grant",
|
|
||||||
"objectName": "abramov@dcconstructions.ru / task-manager",
|
|
||||||
"clientId": null,
|
|
||||||
"result": "success",
|
|
||||||
"details": "Value: member"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_abramov_dcconstructions_ru_5",
|
|
||||||
"at": "2026-05-05T22:43:22.734Z",
|
|
||||||
"actorUserId": "user_root",
|
|
||||||
"actorName": "DC SUDO",
|
|
||||||
"action": "Пользователь синхронизирован в Authentik",
|
|
||||||
"objectType": "user",
|
|
||||||
"objectName": "abramov@dcconstructions.ru",
|
|
||||||
"clientId": null,
|
|
||||||
"result": "success",
|
|
||||||
"details": "Groups: nodedc:launcher:user, nodedc:taskmanager:user"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_support_dcconstructions_ru",
|
|
||||||
"at": "2026-05-06T01:04:54.018Z",
|
|
||||||
"actorUserId": "user_root",
|
|
||||||
"actorName": "DC SUDO",
|
|
||||||
"action": "Создан инвайт",
|
|
||||||
"objectType": "invite",
|
|
||||||
"objectName": "support@dcconstructions.ru",
|
|
||||||
"clientId": "client_romashka",
|
|
||||||
"result": "success",
|
|
||||||
"details": "Роль: member"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_support_dcconstructions_ru_2",
|
|
||||||
"at": "2026-05-06T01:04:56.745Z",
|
|
||||||
"actorUserId": "user_root",
|
|
||||||
"actorName": "DC SUDO",
|
|
||||||
"action": "Обновлён инвайт",
|
|
||||||
"objectType": "invite",
|
|
||||||
"objectName": "support@dcconstructions.ru",
|
|
||||||
"clientId": "client_romashka",
|
|
||||||
"result": "success",
|
|
||||||
"details": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_support_dcconstructions_ru_3",
|
|
||||||
"at": "2026-05-06T01:06:48.113Z",
|
|
||||||
"actorUserId": "user_support_dcconstructions_ru",
|
|
||||||
"actorName": "DC CONSTRICTIONS",
|
|
||||||
"action": "Регистрация по инвайту",
|
|
||||||
"objectType": "invite",
|
|
||||||
"objectName": "support@dcconstructions.ru",
|
|
||||||
"clientId": "client_romashka",
|
|
||||||
"result": "success",
|
|
||||||
"details": "Role: member"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_support_dcconstructions_ru_4",
|
|
||||||
"at": "2026-05-06T01:06:48.121Z",
|
|
||||||
"actorUserId": "user_support_dcconstructions_ru",
|
|
||||||
"actorName": "DC CONSTRICTIONS",
|
|
||||||
"action": "Пользователь синхронизирован в Authentik",
|
|
||||||
"objectType": "user",
|
|
||||||
"objectName": "support@dcconstructions.ru",
|
|
||||||
"clientId": null,
|
|
||||||
"result": "success",
|
|
||||||
"details": "Groups: nodedc:launcher:user"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_support_dcconstructions_ru_task_manager",
|
|
||||||
"at": "2026-05-06T01:28:06.516Z",
|
|
||||||
"actorUserId": "user_root",
|
|
||||||
"actorName": "DC SUDO",
|
|
||||||
"action": "Обновлён доступ пользователя к сервису",
|
|
||||||
"objectType": "grant",
|
|
||||||
"objectName": "support@dcconstructions.ru / task-manager",
|
|
||||||
"clientId": null,
|
|
||||||
"result": "success",
|
|
||||||
"details": "Value: member"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "audit_support_dcconstructions_ru_5",
|
|
||||||
"at": "2026-05-06T01:28:06.887Z",
|
|
||||||
"actorUserId": "user_root",
|
|
||||||
"actorName": "DC SUDO",
|
|
||||||
"action": "Пользователь синхронизирован в Authentik",
|
|
||||||
"objectType": "user",
|
|
||||||
"objectName": "support@dcconstructions.ru",
|
|
||||||
"clientId": null,
|
|
||||||
"result": "success",
|
|
||||||
"details": "Groups: nodedc:launcher:user, nodedc:taskmanager:user"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"settings": {
|
"settings": {
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 101 KiB |
|
|
@ -14,7 +14,6 @@ const serverRoot = dirname(fileURLToPath(import.meta.url));
|
||||||
const projectRoot = resolve(serverRoot, "..");
|
const projectRoot = resolve(serverRoot, "..");
|
||||||
const maxStorageJsonBodyBytes = "260mb";
|
const maxStorageJsonBodyBytes = "260mb";
|
||||||
const pendingLoginTtlMs = 10 * 60 * 1000;
|
const pendingLoginTtlMs = 10 * 60 * 1000;
|
||||||
const serviceHandoffTtlMs = 60 * 1000;
|
|
||||||
const sessionTtlMs = 12 * 60 * 60 * 1000;
|
const sessionTtlMs = 12 * 60 * 60 * 1000;
|
||||||
const oidcStateCookieName = "nodedc_oidc_state";
|
const oidcStateCookieName = "nodedc_oidc_state";
|
||||||
const maxOidcStateCookieEntries = 8;
|
const maxOidcStateCookieEntries = 8;
|
||||||
|
|
@ -33,7 +32,6 @@ const httpServer = createHttpServer(app);
|
||||||
const controlPlaneStore = createControlPlaneStore({ projectRoot });
|
const controlPlaneStore = createControlPlaneStore({ projectRoot });
|
||||||
const authentikSyncClient = createAuthentikSyncClient({ baseUrl: config.authentikBaseUrl, token: config.authentikApiToken });
|
const authentikSyncClient = createAuthentikSyncClient({ baseUrl: config.authentikBaseUrl, token: config.authentikApiToken });
|
||||||
const pendingLogins = new Map();
|
const pendingLogins = new Map();
|
||||||
const serviceHandoffs = new Map();
|
|
||||||
const sessions = new Map();
|
const sessions = new Map();
|
||||||
const runtimeEventClients = new Set();
|
const runtimeEventClients = new Set();
|
||||||
let discoveryCache = null;
|
let discoveryCache = null;
|
||||||
|
|
@ -137,11 +135,22 @@ app.get("/auth/callback", asyncRoute(async (req, res) => {
|
||||||
const discovery = await getOidcDiscovery();
|
const discovery = await getOidcDiscovery();
|
||||||
const tokenSet = await exchangeCodeForTokens(discovery, code, pendingLogin.codeVerifier);
|
const tokenSet = await exchangeCodeForTokens(discovery, code, pendingLogin.codeVerifier);
|
||||||
const claims = await verifyIdToken(discovery, tokenSet.id_token, pendingLogin.nonce);
|
const claims = await verifyIdToken(discovery, tokenSet.id_token, pendingLogin.nonce);
|
||||||
createLauncherSession(res, normalizeUser(claims), {
|
const sessionId = randomBase64Url(48);
|
||||||
idToken: tokenSet.id_token,
|
const session = {
|
||||||
accessToken: tokenSet.access_token ?? null,
|
id: sessionId,
|
||||||
expiresAt: tokenSet.expires_in ? Date.now() + Number(tokenSet.expires_in) * 1000 : null,
|
user: normalizeUser(claims),
|
||||||
});
|
tokenSet: {
|
||||||
|
idToken: tokenSet.id_token,
|
||||||
|
accessToken: tokenSet.access_token ?? null,
|
||||||
|
expiresAt: tokenSet.expires_in ? Date.now() + Number(tokenSet.expires_in) * 1000 : null,
|
||||||
|
},
|
||||||
|
createdAt: Date.now(),
|
||||||
|
expiresAt: Date.now() + sessionTtlMs,
|
||||||
|
};
|
||||||
|
|
||||||
|
pruneExpiredSessions();
|
||||||
|
sessions.set(sessionId, session);
|
||||||
|
res.cookie(sessionCookieName, sessionId, cookieOptions(sessionTtlMs));
|
||||||
res.redirect(pendingLogin.returnTo);
|
res.redirect(pendingLogin.returnTo);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
@ -234,81 +243,6 @@ app.get("/api/apps", (req, res) => {
|
||||||
res.json({ apps: getAppsForSession(session) });
|
res.json({ apps: getAppsForSession(session) });
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get("/api/services/:serviceSlug/launch", requireSession, (req, res) => {
|
|
||||||
const serviceSlug = sanitizeServiceSlug(req.params.serviceSlug);
|
|
||||||
const runtimeContext = getRuntimeSessionContext(req.nodedcSession);
|
|
||||||
const app = getAppsForUser(runtimeContext.groups).find((candidate) => candidate.slug === serviceSlug);
|
|
||||||
|
|
||||||
if (!app || !app.hasAccess || app.status !== "active") {
|
|
||||||
res.status(403).type("text/plain").send("NODE.DC service access denied.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (serviceSlug !== "task-manager") {
|
|
||||||
res.redirect(app.openUrl || app.url || "/");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const handoffToken = createServiceHandoff(serviceSlug, runtimeContext.user);
|
|
||||||
const taskBaseUrl = getTaskBaseUrl();
|
|
||||||
const targetUrl = new URL("/auth/nodedc/handoff/", taskBaseUrl);
|
|
||||||
const nextPath = sanitizeReturnTo(req.query.next_path || req.query.returnTo || "/");
|
|
||||||
|
|
||||||
targetUrl.searchParams.set("token", handoffToken);
|
|
||||||
|
|
||||||
if (nextPath && nextPath !== "/") {
|
|
||||||
targetUrl.searchParams.set("next_path", nextPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.redirect(targetUrl.toString());
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post("/api/internal/handoff/consume", (req, res) => {
|
|
||||||
if (!isInternalRequestAuthorized(req)) {
|
|
||||||
res.status(config.internalAccessToken ? 401 : 503).json({
|
|
||||||
ok: false,
|
|
||||||
error: config.internalAccessToken ? "internal_handoff_unauthorized" : "internal_handoff_not_configured",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const token = typeof req.body?.token === "string" ? req.body.token : "";
|
|
||||||
const serviceSlug = sanitizeServiceSlug(req.body?.serviceSlug);
|
|
||||||
const handoff = consumeServiceHandoff(token, serviceSlug);
|
|
||||||
|
|
||||||
if (!handoff) {
|
|
||||||
res.status(404).json({ ok: false, error: "handoff_not_found" });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const snapshot = controlPlaneStore.getSnapshot({ name: "NODE.DC handoff validation" });
|
|
||||||
const user = findInternalAccessUser(snapshot.data, {
|
|
||||||
subject: handoff.user.sub,
|
|
||||||
email: handoff.user.email,
|
|
||||||
});
|
|
||||||
const groups = user ? resolveRequiredGroups(snapshot.data, user) : handoff.user.groups;
|
|
||||||
const app = getAppsForUser(groups).find((candidate) => candidate.slug === serviceSlug);
|
|
||||||
|
|
||||||
if (!user || !app?.hasAccess) {
|
|
||||||
res.status(403).json({ ok: false, error: "handoff_access_denied" });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
ok: true,
|
|
||||||
serviceSlug,
|
|
||||||
user: {
|
|
||||||
id: user.id,
|
|
||||||
email: user.email,
|
|
||||||
name: user.name,
|
|
||||||
avatarUrl: user.avatarUrl ?? null,
|
|
||||||
subject: user.authentikUserId || handoff.user.sub,
|
|
||||||
authentikUserId: user.authentikUserId ?? null,
|
|
||||||
groups,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get("/api/profile", requireSession, (req, res) => {
|
app.get("/api/profile", requireSession, (req, res) => {
|
||||||
const { actor, data } = getLauncherProfileContext(req.nodedcSession);
|
const { actor, data } = getLauncherProfileContext(req.nodedcSession);
|
||||||
const user = findLauncherUser(data, actor.id);
|
const user = findLauncherUser(data, actor.id);
|
||||||
|
|
@ -462,17 +396,6 @@ app.post("/api/invites/:token/register", asyncRoute(async (req, res) => {
|
||||||
email: result.user.email,
|
email: result.user.email,
|
||||||
name: result.user.name,
|
name: result.user.name,
|
||||||
});
|
});
|
||||||
const groups = resolveRequiredGroups(storeResult.data, storeResult.user);
|
|
||||||
|
|
||||||
createLauncherSession(
|
|
||||||
res,
|
|
||||||
normalizeControlPlaneSessionUser(storeResult.user, groups),
|
|
||||||
{
|
|
||||||
idToken: null,
|
|
||||||
accessToken: null,
|
|
||||||
expiresAt: null,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
publishControlPlaneEvent("invite.registered", [result.user.id]);
|
publishControlPlaneEvent("invite.registered", [result.user.id]);
|
||||||
res.json({
|
res.json({
|
||||||
|
|
@ -480,9 +403,7 @@ app.post("/api/invites/:token/register", asyncRoute(async (req, res) => {
|
||||||
user: storeResult.user,
|
user: storeResult.user,
|
||||||
data: storeResult.data,
|
data: storeResult.data,
|
||||||
provisioning: toProvisioningResponse(provisionedUser),
|
provisioning: toProvisioningResponse(provisionedUser),
|
||||||
loginUrl: buildLoginRedirectUrl("/", { forceLogin: true, includeReturnTo: true }),
|
loginUrl: buildLoginRedirectUrl("/", { forceLogin: true }),
|
||||||
redirectUrl: "/",
|
|
||||||
authenticated: true,
|
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
@ -888,37 +809,6 @@ function normalizeUser(claims) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeControlPlaneSessionUser(user, groups) {
|
|
||||||
return {
|
|
||||||
sub: String(user.authentikUserId || user.id),
|
|
||||||
email: user.email,
|
|
||||||
name: user.name,
|
|
||||||
preferredUsername: user.email,
|
|
||||||
avatarUrl: user.avatarUrl ?? null,
|
|
||||||
groups,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createLauncherSession(res, user, tokenSet = {}) {
|
|
||||||
const sessionId = randomBase64Url(48);
|
|
||||||
const session = {
|
|
||||||
id: sessionId,
|
|
||||||
user,
|
|
||||||
tokenSet: {
|
|
||||||
idToken: tokenSet.idToken ?? null,
|
|
||||||
accessToken: tokenSet.accessToken ?? null,
|
|
||||||
expiresAt: tokenSet.expiresAt ?? null,
|
|
||||||
},
|
|
||||||
createdAt: Date.now(),
|
|
||||||
expiresAt: Date.now() + sessionTtlMs,
|
|
||||||
};
|
|
||||||
|
|
||||||
pruneExpiredSessions();
|
|
||||||
sessions.set(sessionId, session);
|
|
||||||
res.cookie(sessionCookieName, sessionId, cookieOptions(sessionTtlMs));
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
|
|
||||||
function firstStringClaim(...values) {
|
function firstStringClaim(...values) {
|
||||||
for (const value of values) {
|
for (const value of values) {
|
||||||
if (typeof value === "string" && value) return value;
|
if (typeof value === "string" && value) return value;
|
||||||
|
|
@ -1176,64 +1066,13 @@ function specialRequiredGroups(slug) {
|
||||||
|
|
||||||
function getServiceUrl(service) {
|
function getServiceUrl(service) {
|
||||||
if (service.slug === "task-manager") {
|
if (service.slug === "task-manager") {
|
||||||
return `/api/services/${encodeURIComponent(service.slug)}/launch`;
|
const taskBaseUrl = process.env.TASK_BASE_URL ?? `http://${process.env.TASK_DOMAIN ?? "task.local.nodedc"}`;
|
||||||
|
return `${taskBaseUrl.replace(/\/$/, "")}/auth/oidc/login/`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return service.launchUrl || service.url || "#";
|
return service.launchUrl || service.url || "#";
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTaskBaseUrl() {
|
|
||||||
const taskBaseUrl = process.env.TASK_BASE_URL ?? `http://${process.env.TASK_DOMAIN ?? "task.local.nodedc"}`;
|
|
||||||
return taskBaseUrl.replace(/\/$/, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
function createServiceHandoff(serviceSlug, user) {
|
|
||||||
pruneExpiredServiceHandoffs();
|
|
||||||
|
|
||||||
const token = randomBase64Url(48);
|
|
||||||
serviceHandoffs.set(token, {
|
|
||||||
serviceSlug,
|
|
||||||
user: {
|
|
||||||
sub: user.sub,
|
|
||||||
email: user.email,
|
|
||||||
name: user.name,
|
|
||||||
preferredUsername: user.preferredUsername ?? user.email,
|
|
||||||
avatarUrl: user.avatarUrl ?? null,
|
|
||||||
groups: user.groups ?? [],
|
|
||||||
},
|
|
||||||
expiresAt: Date.now() + serviceHandoffTtlMs,
|
|
||||||
});
|
|
||||||
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
function consumeServiceHandoff(token, serviceSlug) {
|
|
||||||
pruneExpiredServiceHandoffs();
|
|
||||||
|
|
||||||
if (!token) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const handoff = serviceHandoffs.get(token);
|
|
||||||
serviceHandoffs.delete(token);
|
|
||||||
|
|
||||||
if (!handoff || handoff.expiresAt < Date.now() || handoff.serviceSlug !== serviceSlug) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return handoff;
|
|
||||||
}
|
|
||||||
|
|
||||||
function pruneExpiredServiceHandoffs() {
|
|
||||||
const now = Date.now();
|
|
||||||
|
|
||||||
for (const [token, handoff] of serviceHandoffs.entries()) {
|
|
||||||
if (!handoff || handoff.expiresAt < now) {
|
|
||||||
serviceHandoffs.delete(token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFrontchannelLogoutUrls() {
|
function getFrontchannelLogoutUrls() {
|
||||||
const urls = [config.taskLogoutUrl];
|
const urls = [config.taskLogoutUrl];
|
||||||
const launcherData = readLauncherData();
|
const launcherData = readLauncherData();
|
||||||
|
|
@ -1782,7 +1621,7 @@ function setNoStore(res) {
|
||||||
res.setHeader("Expires", "0");
|
res.setHeader("Expires", "0");
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildLoginRedirectUrl(returnTo, { forceLogin = false, includeReturnTo = false } = {}) {
|
function buildLoginRedirectUrl(returnTo, { forceLogin = false } = {}) {
|
||||||
const loginUrl = new URL("/auth/login", config.appBaseUrl);
|
const loginUrl = new URL("/auth/login", config.appBaseUrl);
|
||||||
const cleanReturnTo = sanitizeReturnTo(returnTo);
|
const cleanReturnTo = sanitizeReturnTo(returnTo);
|
||||||
|
|
||||||
|
|
@ -1790,7 +1629,7 @@ function buildLoginRedirectUrl(returnTo, { forceLogin = false, includeReturnTo =
|
||||||
loginUrl.searchParams.set("prompt", "login");
|
loginUrl.searchParams.set("prompt", "login");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (includeReturnTo || cleanReturnTo !== "/") {
|
if (cleanReturnTo !== "/") {
|
||||||
loginUrl.searchParams.set("returnTo", cleanReturnTo);
|
loginUrl.searchParams.set("returnTo", cleanReturnTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -475,7 +475,6 @@ export function LauncherApp() {
|
||||||
|
|
||||||
setData(syncLauncherServiceLinks(result.data));
|
setData(syncLauncherServiceLinks(result.data));
|
||||||
setInviteFlow({ status: "registered", payload, loginUrl: result.loginUrl });
|
setInviteFlow({ status: "registered", payload, loginUrl: result.loginUrl });
|
||||||
window.location.replace(result.redirectUrl || "/");
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setInviteFlow({
|
setInviteFlow({
|
||||||
status: "error",
|
status: "error",
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,6 @@ export interface RegisterInviteCommand {
|
||||||
|
|
||||||
export interface RegisterInviteResponse extends AcceptInviteResponse {
|
export interface RegisterInviteResponse extends AcceptInviteResponse {
|
||||||
loginUrl: string;
|
loginUrl: string;
|
||||||
redirectUrl: string;
|
|
||||||
authenticated: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchPublicInvite(token: string): Promise<PublicInviteResponse> {
|
export async function fetchPublicInvite(token: string): Promise<PublicInviteResponse> {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue