@efa-one/sdk 1.10.1

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 (43) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +5 -0
  3. package/README.md +97 -0
  4. package/backend/apiRegistry.d.ts +22 -0
  5. package/backend/apiRegistry.js +62 -0
  6. package/backend/audit.d.ts +1 -0
  7. package/backend/audit.js +54 -0
  8. package/backend/auth.d.ts +31 -0
  9. package/backend/auth.js +233 -0
  10. package/backend/customPermissions.d.ts +11 -0
  11. package/backend/customPermissions.js +23 -0
  12. package/backend/health.d.ts +3 -0
  13. package/backend/health.js +23 -0
  14. package/backend/i18n-backend.d.ts +2 -0
  15. package/backend/i18n-backend.js +21 -0
  16. package/backend/index.d.ts +14 -0
  17. package/backend/index.js +30 -0
  18. package/backend/mail.d.ts +18 -0
  19. package/backend/mail.js +43 -0
  20. package/backend/notifications.d.ts +9 -0
  21. package/backend/notifications.js +24 -0
  22. package/backend/permissionCheck.d.ts +1 -0
  23. package/backend/permissionCheck.js +12 -0
  24. package/backend/permissionClient.d.ts +1 -0
  25. package/backend/permissionClient.js +11 -0
  26. package/backend/permissions.d.ts +11 -0
  27. package/backend/permissions.js +66 -0
  28. package/backend/reporting.d.ts +6 -0
  29. package/backend/reporting.js +31 -0
  30. package/backend/serviceClient.d.ts +17 -0
  31. package/backend/serviceClient.js +77 -0
  32. package/backend/serviceDiscovery.d.ts +28 -0
  33. package/backend/serviceDiscovery.js +57 -0
  34. package/frontend/DevHeader.d.ts +8 -0
  35. package/frontend/DevHeader.js +42 -0
  36. package/frontend/i18n.d.ts +3 -0
  37. package/frontend/i18n.js +16 -0
  38. package/frontend/index.d.ts +3 -0
  39. package/frontend/index.js +3 -0
  40. package/frontend/ipc.d.ts +11 -0
  41. package/frontend/ipc.js +39 -0
  42. package/frontend/package.json +1 -0
  43. package/package.json +96 -0
@@ -0,0 +1,233 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.requireAuth = requireAuth;
7
+ exports.requireAdmin = requireAdmin;
8
+ exports.requirePermission = requirePermission;
9
+ exports.requireAdminOrPermission = requireAdminOrPermission;
10
+ exports.requireRegistryKey = requireRegistryKey;
11
+ exports.requireInternalOrAuth = requireInternalOrAuth;
12
+ exports.requireInternalOrAdminOrPermission = requireInternalOrAdminOrPermission;
13
+ exports.createExchangeRouter = createExchangeRouter;
14
+ const express_1 = require("express");
15
+ const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
16
+ const permissionClient_1 = require("./permissionClient");
17
+ const JWT_ISSUER = 'converge';
18
+ const JWT_AUDIENCE = 'converge';
19
+ function getPlatformPublicKey() {
20
+ const s = process.env.JWT_PUBLIC_KEY;
21
+ if (!s)
22
+ throw new Error('JWT_PUBLIC_KEY environment variable is required');
23
+ return Buffer.from(s, 'base64').toString('utf8');
24
+ }
25
+ function getSessionSecret() {
26
+ const s = process.env.APP_SESSION_SECRET;
27
+ if (!s)
28
+ throw new Error('APP_SESSION_SECRET environment variable is required');
29
+ return s;
30
+ }
31
+ function getCookieName() {
32
+ return process.env.APP_SESSION_COOKIE_NAME || 'app_session';
33
+ }
34
+ function setSessionCookie(res, token) {
35
+ const secure = process.env.COOKIE_SECURE
36
+ ? ['1', 'true', 'yes', 'on'].includes(process.env.COOKIE_SECURE.toLowerCase())
37
+ : process.env.NODE_ENV === 'production';
38
+ res.cookie(getCookieName(), token, {
39
+ httpOnly: true,
40
+ sameSite: 'lax',
41
+ maxAge: 8 * 60 * 60 * 1000,
42
+ secure,
43
+ path: '/',
44
+ });
45
+ }
46
+ function requireAuth(req, res, next) {
47
+ const token = req.cookies?.[getCookieName()];
48
+ if (!token) {
49
+ res.status(401).json({ error: 'Not authenticated' });
50
+ return;
51
+ }
52
+ try {
53
+ const payload = jsonwebtoken_1.default.verify(token, getSessionSecret(), { algorithms: ['HS256'] });
54
+ req.user = payload;
55
+ next();
56
+ }
57
+ catch {
58
+ res.status(401).json({ error: 'Session invalid or expired' });
59
+ }
60
+ }
61
+ function requireAdmin(req, res, next) {
62
+ requireAuth(req, res, () => {
63
+ (0, permissionClient_1.getUserPermissions)(req.user.convergeId)
64
+ .then((keys) => {
65
+ if (!keys.includes('converge-admin')) {
66
+ res.status(403).json({ error: 'Admin access required' });
67
+ return;
68
+ }
69
+ next();
70
+ })
71
+ .catch((err) => {
72
+ console.error(JSON.stringify({ level: 'error', msg: 'requireAdmin: permission lookup failed', err: String(err) }));
73
+ res.status(503).json({ error: 'Permission service unavailable' });
74
+ });
75
+ });
76
+ }
77
+ function requirePermission(permission) {
78
+ return (req, res, next) => {
79
+ requireAuth(req, res, () => {
80
+ (0, permissionClient_1.getUserPermissions)(req.user.convergeId)
81
+ .then((keys) => {
82
+ if (!keys.includes(permission)) {
83
+ res.status(403).json({ error: `Permission required: ${permission}` });
84
+ return;
85
+ }
86
+ next();
87
+ })
88
+ .catch((err) => {
89
+ console.error(JSON.stringify({ level: 'error', msg: 'requirePermission: permission lookup failed', err: String(err) }));
90
+ res.status(503).json({ error: 'Permission service unavailable' });
91
+ });
92
+ });
93
+ };
94
+ }
95
+ function requireAdminOrPermission(...permissionKeys) {
96
+ return (req, res, next) => {
97
+ requireAuth(req, res, () => {
98
+ (0, permissionClient_1.getUserPermissions)(req.user.convergeId)
99
+ .then((keys) => {
100
+ if (keys.includes('converge-admin')) {
101
+ next();
102
+ return;
103
+ }
104
+ if (permissionKeys.some((k) => keys.includes(k))) {
105
+ next();
106
+ return;
107
+ }
108
+ res.status(403).json({ error: 'Forbidden – missing permission' });
109
+ })
110
+ .catch((err) => {
111
+ console.error(JSON.stringify({ level: 'error', msg: 'requireAdminOrPermission: permission lookup failed', err: String(err) }));
112
+ res.status(503).json({ error: 'Permission service unavailable' });
113
+ });
114
+ });
115
+ };
116
+ }
117
+ const PROVENANCE_ISSUER = 'converge-gateway';
118
+ function hasValidServiceProvenance(req) {
119
+ const token = req.headers['x-service-token'];
120
+ if (!token)
121
+ return false;
122
+ const audience = process.env.SERVICE_KEY;
123
+ if (!audience)
124
+ return false;
125
+ try {
126
+ jsonwebtoken_1.default.verify(token, getPlatformPublicKey(), {
127
+ algorithms: ['RS256'],
128
+ issuer: PROVENANCE_ISSUER,
129
+ audience,
130
+ });
131
+ return true;
132
+ }
133
+ catch {
134
+ return false;
135
+ }
136
+ }
137
+ function requireRegistryKey(req, res, next) {
138
+ if (hasValidServiceProvenance(req)) {
139
+ next();
140
+ return;
141
+ }
142
+ res.status(401).json({ error: 'Service provenance token required' });
143
+ }
144
+ function requireInternalOrAuth(req, res, next) {
145
+ if (hasValidServiceProvenance(req)) {
146
+ next();
147
+ return;
148
+ }
149
+ requireAuth(req, res, next);
150
+ }
151
+ function requireInternalOrAdminOrPermission(...permissionKeys) {
152
+ return (req, res, next) => {
153
+ if (hasValidServiceProvenance(req)) {
154
+ next();
155
+ return;
156
+ }
157
+ requireAdminOrPermission(...permissionKeys)(req, res, next);
158
+ };
159
+ }
160
+ function createExchangeRouter(pool) {
161
+ const router = (0, express_1.Router)();
162
+ router.post('/exchange', async (req, res) => {
163
+ const { token } = req.body;
164
+ if (!token) {
165
+ res.status(400).json({ error: 'token required' });
166
+ return;
167
+ }
168
+ let convergePayload;
169
+ try {
170
+ convergePayload = jsonwebtoken_1.default.verify(token, getPlatformPublicKey(), {
171
+ algorithms: ['RS256'],
172
+ issuer: JWT_ISSUER,
173
+ audience: JWT_AUDIENCE,
174
+ });
175
+ }
176
+ catch {
177
+ res.status(401).json({ error: 'Invalid or expired platform token' });
178
+ return;
179
+ }
180
+ try {
181
+ const { rows } = await pool.query(`INSERT INTO app_users (converge_id, email, name, last_seen_at)
182
+ VALUES ($1, $2, $3, NOW())
183
+ ON CONFLICT (converge_id) DO UPDATE
184
+ SET last_seen_at = NOW(),
185
+ email = EXCLUDED.email,
186
+ name = EXCLUDED.name
187
+ RETURNING id`, [
188
+ convergePayload.sub,
189
+ convergePayload.email ?? null,
190
+ convergePayload.name,
191
+ ]);
192
+ const appUser = rows[0];
193
+ const sessionPayload = {
194
+ sub: appUser.id,
195
+ convergeId: convergePayload.sub,
196
+ name: convergePayload.name,
197
+ email: convergePayload.email ?? null,
198
+ language: convergePayload.language,
199
+ tenant: convergePayload.tenant,
200
+ };
201
+ const sessionToken = jsonwebtoken_1.default.sign(sessionPayload, getSessionSecret(), { algorithm: 'HS256', expiresIn: '8h' });
202
+ setSessionCookie(res, sessionToken);
203
+ res.json({
204
+ user: {
205
+ id: appUser.id,
206
+ convergeId: convergePayload.sub,
207
+ name: convergePayload.name,
208
+ email: convergePayload.email ?? null,
209
+ tenant: convergePayload.tenant,
210
+ },
211
+ });
212
+ }
213
+ catch (err) {
214
+ console.error(JSON.stringify({ level: 'error', msg: 'Exchange error', err: String(err) }));
215
+ res.status(500).json({ error: 'Internal server error' });
216
+ }
217
+ });
218
+ router.post('/logout', (_req, res) => {
219
+ res.clearCookie(getCookieName());
220
+ res.json({ ok: true });
221
+ });
222
+ router.get('/permissions', requireAuth, async (req, res) => {
223
+ try {
224
+ const keys = await (0, permissionClient_1.getUserPermissions)(req.user.convergeId);
225
+ res.json({ keys });
226
+ }
227
+ catch (err) {
228
+ console.error(JSON.stringify({ level: 'error', msg: 'Permissions lookup failed', err: String(err) }));
229
+ res.status(503).json({ error: 'Permission service unavailable' });
230
+ }
231
+ });
232
+ return router;
233
+ }
@@ -0,0 +1,11 @@
1
+ export interface CustomPermissionInput {
2
+ serviceKey: string;
3
+ key: string;
4
+ displayName: string;
5
+ }
6
+ export interface CustomPermissionResult {
7
+ id: string;
8
+ key: string;
9
+ }
10
+ export declare function createCustomPermission(input: CustomPermissionInput): Promise<CustomPermissionResult>;
11
+ export declare function deleteCustomPermission(id: string): Promise<void>;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createCustomPermission = createCustomPermission;
4
+ exports.deleteCustomPermission = deleteCustomPermission;
5
+ const serviceClient_1 = require("./serviceClient");
6
+ async function createCustomPermission(input) {
7
+ if (!input.serviceKey)
8
+ throw new Error('createCustomPermission: serviceKey required');
9
+ if (!input.key)
10
+ throw new Error('createCustomPermission: key required');
11
+ if (!input.displayName)
12
+ throw new Error('createCustomPermission: displayName required');
13
+ return serviceClient_1.serviceClient.call('converge_access', 'POST', '/api/internal/permissions', {
14
+ serviceKey: input.serviceKey,
15
+ key: input.key,
16
+ displayName: input.displayName,
17
+ });
18
+ }
19
+ async function deleteCustomPermission(id) {
20
+ if (!id)
21
+ throw new Error('deleteCustomPermission: id required');
22
+ await serviceClient_1.serviceClient.call('converge_access', 'DELETE', `/api/internal/permissions/${encodeURIComponent(id)}`);
23
+ }
@@ -0,0 +1,3 @@
1
+ import { Router } from 'express';
2
+ import { Pool } from 'pg';
3
+ export declare function createHealthRouter(pool: Pool): Router;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createHealthRouter = createHealthRouter;
4
+ const express_1 = require("express");
5
+ function createHealthRouter(pool) {
6
+ const router = (0, express_1.Router)();
7
+ router.get('/', (_req, res) => {
8
+ res.json({
9
+ status: 'ok',
10
+ version: process.env.npm_package_version ?? 'unknown',
11
+ });
12
+ });
13
+ router.get('/ready', async (_req, res) => {
14
+ try {
15
+ await pool.query('SELECT 1');
16
+ res.json({ status: 'ok' });
17
+ }
18
+ catch (err) {
19
+ res.json({ status: 'degraded', error: 'Database unreachable' });
20
+ }
21
+ });
22
+ return router;
23
+ }
@@ -0,0 +1,2 @@
1
+ export declare function loadLocales(localesDir: string, languages?: string[]): void;
2
+ export declare function t(key: string, lang?: string): string;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loadLocales = loadLocales;
4
+ exports.t = t;
5
+ const fs_1 = require("fs");
6
+ const path_1 = require("path");
7
+ const locales = {};
8
+ function loadLocales(localesDir, languages = ['de', 'en']) {
9
+ for (const lang of languages) {
10
+ try {
11
+ const data = (0, fs_1.readFileSync)((0, path_1.join)(localesDir, `${lang}.json`), 'utf-8');
12
+ locales[lang] = JSON.parse(data);
13
+ }
14
+ catch {
15
+ console.warn(`i18n: locale file for '${lang}' not found in ${localesDir}`);
16
+ }
17
+ }
18
+ }
19
+ function t(key, lang = 'de') {
20
+ return locales[lang]?.[key] ?? locales['de']?.[key] ?? key;
21
+ }
@@ -0,0 +1,14 @@
1
+ export * from './auth';
2
+ export * from './health';
3
+ export * from './audit';
4
+ export * from './reporting';
5
+ export * from './mail';
6
+ export * from './notifications';
7
+ export * from './permissions';
8
+ export * from './permissionClient';
9
+ export * from './permissionCheck';
10
+ export * from './customPermissions';
11
+ export * from './serviceClient';
12
+ export * from './serviceDiscovery';
13
+ export * from './apiRegistry';
14
+ export * from './i18n-backend';
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./auth"), exports);
18
+ __exportStar(require("./health"), exports);
19
+ __exportStar(require("./audit"), exports);
20
+ __exportStar(require("./reporting"), exports);
21
+ __exportStar(require("./mail"), exports);
22
+ __exportStar(require("./notifications"), exports);
23
+ __exportStar(require("./permissions"), exports);
24
+ __exportStar(require("./permissionClient"), exports);
25
+ __exportStar(require("./permissionCheck"), exports);
26
+ __exportStar(require("./customPermissions"), exports);
27
+ __exportStar(require("./serviceClient"), exports);
28
+ __exportStar(require("./serviceDiscovery"), exports);
29
+ __exportStar(require("./apiRegistry"), exports);
30
+ __exportStar(require("./i18n-backend"), exports);
@@ -0,0 +1,18 @@
1
+ export interface MailAttachmentInput {
2
+ filename: string;
3
+ content_type: string;
4
+ data_b64: string;
5
+ }
6
+ export interface SendMailOptions {
7
+ to: string[];
8
+ cc?: string[];
9
+ bcc?: string[];
10
+ from?: string;
11
+ reply_to?: string;
12
+ subject: string;
13
+ body_text?: string;
14
+ body_html?: string;
15
+ attachments?: MailAttachmentInput[];
16
+ scheduled_for?: string;
17
+ }
18
+ export declare function sendMail(options: SendMailOptions): void;
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sendMail = sendMail;
4
+ function resolveUrl() {
5
+ if (process.env.MAIL_URL)
6
+ return process.env.MAIL_URL;
7
+ const gateway = process.env.CONVERGE_GATEWAY_URL;
8
+ if (gateway) {
9
+ return `${gateway.replace(/\/$/, '')}/api/gateway/converge_mail/api/mail/send`;
10
+ }
11
+ return null;
12
+ }
13
+ function sendMail(options) {
14
+ const url = resolveUrl();
15
+ if (!url)
16
+ return;
17
+ const headers = { 'Content-Type': 'application/json' };
18
+ const registryKey = process.env.SERVICE_AUTH_KEY;
19
+ if (registryKey)
20
+ headers['X-Service-Auth-Key'] = registryKey;
21
+ const sourceService = process.env.SERVICE_KEY ?? process.env.APP_NAME ?? 'unknown';
22
+ headers['X-Source-Service'] = sourceService;
23
+ const body = JSON.stringify({
24
+ to: options.to,
25
+ cc: options.cc ?? [],
26
+ bcc: options.bcc ?? [],
27
+ from: options.from,
28
+ reply_to: options.reply_to,
29
+ subject: options.subject,
30
+ body_text: options.body_text,
31
+ body_html: options.body_html,
32
+ attachments: options.attachments,
33
+ scheduled_for: options.scheduled_for,
34
+ });
35
+ fetch(url, { method: 'POST', headers, body }).catch((err) => {
36
+ console.error(JSON.stringify({
37
+ level: 'error',
38
+ msg: 'Mail send failed',
39
+ subject: options.subject,
40
+ err: String(err),
41
+ }));
42
+ });
43
+ }
@@ -0,0 +1,9 @@
1
+ export type NotificationType = 'info' | 'warning' | 'error';
2
+ export interface SendNotificationOptions {
3
+ userId: string;
4
+ type?: NotificationType;
5
+ title: string;
6
+ body?: string;
7
+ link?: string;
8
+ }
9
+ export declare function sendNotification(options: SendNotificationOptions): void;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sendNotification = sendNotification;
4
+ function sendNotification(options) {
5
+ const url = process.env.NOTIFICATION_URL;
6
+ if (!url)
7
+ return;
8
+ const headers = { 'Content-Type': 'application/json' };
9
+ const registryKey = process.env.SERVICE_AUTH_KEY;
10
+ if (registryKey) {
11
+ headers['X-Service-Auth-Key'] = registryKey;
12
+ }
13
+ const body = JSON.stringify({
14
+ userId: options.userId,
15
+ type: options.type ?? 'info',
16
+ title: options.title,
17
+ body: options.body ?? null,
18
+ link: options.link ?? null,
19
+ sourceApp: process.env.APP_NAME ?? 'app',
20
+ });
21
+ fetch(url, { method: 'POST', headers, body }).catch((err) => {
22
+ console.error(JSON.stringify({ level: 'error', msg: 'Notification send failed', title: options.title, err: String(err) }));
23
+ });
24
+ }
@@ -0,0 +1 @@
1
+ export declare function checkPermission(convergeId: string, key: string, sourceApp?: string): Promise<boolean>;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkPermission = checkPermission;
4
+ const serviceClient_1 = require("./serviceClient");
5
+ async function checkPermission(convergeId, key, sourceApp) {
6
+ if (!convergeId)
7
+ throw new Error('checkPermission: convergeId required');
8
+ if (!key)
9
+ throw new Error('checkPermission: key required');
10
+ const { granted } = await serviceClient_1.serviceClient.call('converge_access', 'POST', '/api/internal/check-permission', { convergeId, key, sourceApp: sourceApp ?? process.env.APP_NAME ?? null });
11
+ return Boolean(granted);
12
+ }
@@ -0,0 +1 @@
1
+ export declare function getUserPermissions(convergeId: string): Promise<string[]>;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getUserPermissions = getUserPermissions;
4
+ const serviceClient_1 = require("./serviceClient");
5
+ async function getUserPermissions(convergeId) {
6
+ if (!convergeId) {
7
+ throw new Error('getUserPermissions: convergeId required');
8
+ }
9
+ const { keys } = await serviceClient_1.serviceClient.call('converge_access', 'GET', `/api/internal/users/${encodeURIComponent(convergeId)}/permissions`);
10
+ return keys ?? [];
11
+ }
@@ -0,0 +1,11 @@
1
+ export interface AppPermission {
2
+ key: string;
3
+ displayName: string;
4
+ level: number;
5
+ }
6
+ export declare function registerPermissions(serviceKey: string, permissions: AppPermission[], options?: {
7
+ registryBaseUrl?: string;
8
+ retries?: number;
9
+ retryDelayMs?: number;
10
+ fetchImpl?: typeof fetch;
11
+ }): Promise<void>;
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerPermissions = registerPermissions;
4
+ function log(level, msg, extra) {
5
+ console.log(JSON.stringify({ level, msg, timestamp: new Date().toISOString(), ...extra }));
6
+ }
7
+ async function registerPermissions(serviceKey, permissions, options) {
8
+ if (permissions.length === 0)
9
+ return;
10
+ const gatewayUrl = (process.env.CONVERGE_GATEWAY_URL ?? '').replace(/\/$/, '');
11
+ const legacyUrl = (options?.registryBaseUrl ?? process.env.CONVERGE_REGISTRY_URL ?? '').replace(/\/$/, '');
12
+ let url;
13
+ if (gatewayUrl) {
14
+ url = `${gatewayUrl}/api/gateway/converge_access/api/internal/register`;
15
+ }
16
+ else if (legacyUrl) {
17
+ url = `${legacyUrl}/api/registry/services/${encodeURIComponent(serviceKey)}/permissions`;
18
+ }
19
+ else {
20
+ log('warn', 'registerPermissions: Weder CONVERGE_GATEWAY_URL noch CONVERGE_REGISTRY_URL gesetzt – übersprungen');
21
+ return;
22
+ }
23
+ const maxRetries = options?.retries ?? 3;
24
+ const retryDelay = options?.retryDelayMs ?? 5000;
25
+ const fetchImpl = options?.fetchImpl ?? fetch;
26
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
27
+ try {
28
+ const headers = { 'Content-Type': 'application/json', Accept: 'application/json' };
29
+ const registryKey = process.env.SERVICE_AUTH_KEY;
30
+ if (registryKey)
31
+ headers['X-Service-Auth-Key'] = registryKey;
32
+ const res = await fetchImpl(url, {
33
+ method: 'POST',
34
+ headers,
35
+ body: JSON.stringify({ serviceKey, permissions }),
36
+ });
37
+ if (res.ok) {
38
+ const body = await res.json();
39
+ log('info', 'Permissions registriert', {
40
+ serviceKey,
41
+ registered: body.registered,
42
+ });
43
+ return;
44
+ }
45
+ const errText = await res.text().catch(() => '');
46
+ log('warn', 'registerPermissions: Plattform antwortete mit Fehler', {
47
+ status: res.status,
48
+ body: errText,
49
+ attempt,
50
+ });
51
+ }
52
+ catch (err) {
53
+ log('warn', 'registerPermissions: Netzwerkfehler', {
54
+ err: String(err),
55
+ attempt,
56
+ });
57
+ }
58
+ if (attempt < maxRetries) {
59
+ await new Promise((r) => setTimeout(r, retryDelay));
60
+ }
61
+ }
62
+ log('error', 'registerPermissions: Konnte Permissions nicht registrieren nach allen Versuchen', {
63
+ serviceKey,
64
+ maxRetries,
65
+ });
66
+ }
@@ -0,0 +1,6 @@
1
+ type LogLevel = 'debug' | 'info' | 'warn' | 'error';
2
+ export declare function reportEvent(eventType: string, payload?: Record<string, unknown>, token?: string): void;
3
+ export declare function reportLog(level: LogLevel, message: string, context?: Record<string, unknown>, token?: string): void;
4
+ export declare function reportMetric(metricName: string, value: number, dimensions?: Record<string, unknown>, token?: string): void;
5
+ export declare function reportActivity(action: string, token?: string, userId?: string, resource?: string): void;
6
+ export {};
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.reportEvent = reportEvent;
4
+ exports.reportLog = reportLog;
5
+ exports.reportMetric = reportMetric;
6
+ exports.reportActivity = reportActivity;
7
+ function ingest(entries, token) {
8
+ const url = process.env.REPORTING_URL;
9
+ if (!url)
10
+ return;
11
+ const headers = { 'Content-Type': 'application/json' };
12
+ if (token)
13
+ headers['Authorization'] = `Bearer ${token}`;
14
+ fetch(url, { method: 'POST', headers, body: JSON.stringify(entries) }).catch((err) => {
15
+ console.error(JSON.stringify({ level: 'error', msg: 'Reporting ingest failed', err: String(err) }));
16
+ });
17
+ }
18
+ const appName = () => process.env.APP_NAME ?? 'app';
19
+ const appVersion = () => process.env.APP_VERSION ?? 'dev';
20
+ function reportEvent(eventType, payload, token) {
21
+ ingest([{ type: 'event', sourceApp: appName(), appVersion: appVersion(), eventType, payload }], token);
22
+ }
23
+ function reportLog(level, message, context, token) {
24
+ ingest([{ type: 'log', sourceApp: appName(), appVersion: appVersion(), level, message, context }], token);
25
+ }
26
+ function reportMetric(metricName, value, dimensions, token) {
27
+ ingest([{ type: 'metric', sourceApp: appName(), appVersion: appVersion(), metricName, value, dimensions }], token);
28
+ }
29
+ function reportActivity(action, token, userId, resource) {
30
+ ingest([{ type: 'activity', sourceApp: appName(), appVersion: appVersion(), action, resource }], token);
31
+ }
@@ -0,0 +1,17 @@
1
+ export interface ServiceClientOptions {
2
+ gatewayUrl?: string;
3
+ registryKey?: string;
4
+ fetchImpl?: typeof fetch;
5
+ retries?: number;
6
+ retryDelayMs?: number;
7
+ }
8
+ export declare class ServiceClient {
9
+ private gatewayUrl;
10
+ private registryKey;
11
+ private fetchImpl;
12
+ private retries;
13
+ private retryDelay;
14
+ constructor(options?: ServiceClientOptions);
15
+ call<T = unknown>(serviceKey: string, method: string, path: string, body?: unknown): Promise<T>;
16
+ }
17
+ export declare const serviceClient: ServiceClient;