@getstrata/core 0.5.63 → 0.5.65
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/CHANGELOG.md +10 -0
- package/README.md +1 -1
- package/dist/core/jobs/dispatchWebhookJob.d.ts +1 -16
- package/dist/core/queue/redisQueue.d.ts +3 -3
- package/dist/core/runtime/appKeyPrefix.d.ts +9 -0
- package/dist/entries/audit/exportAuditLogs.js +30 -2
- package/dist/entries/audit/siemFormatter.js +30 -2
- package/dist/entries/auth/oauth/providers.js +29 -1
- package/dist/entries/auth/oauth/samlProvider.js +30 -2
- package/dist/entries/cache/createCacheStore.js +44 -10
- package/dist/entries/facades.js +29 -1
- package/dist/entries/http/loginThrottleMiddleware.js +29 -1
- package/dist/entries/http/memoryThrottleMiddleware.js +29 -1
- package/dist/entries/http/scimThrottleMiddleware.js +29 -1
- package/dist/entries/http/throttleMiddleware.js +29 -1
- package/dist/entries/jobs/dispatchWebhookJob.js +11 -266
- package/dist/entries/jobs/exportAuditLogsJob.js +30 -2
- package/dist/entries/mail/mailer.js +29 -1
- package/dist/entries/queue/createAppQueue.js +45 -6
- package/dist/entries/queue/publicQueue.js +45 -6
- package/dist/entries/queue/queueMetrics.js +45 -6
- package/dist/entries/queue/redisQueue.js +43 -6
- package/dist/entries/runtime/appKeyPrefix.js +38 -0
- package/dist/entries/tracing/tracingMiddleware.js +31 -1
- package/dist/index.js +53 -22
- package/dist/modules/webhook/dispatchWebhookJob.d.ts +16 -0
- package/package.json +7 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @getstrata/core changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.65
|
|
4
|
+
|
|
5
|
+
- Identity helpers on `@getstrata/core/runtime/appKeyPrefix` (`smtpEhloHost`, `siemEventType`, `appUserAgent`, `otelServiceName`, `appDisplayName`, `webhookSignatureHeader`) so sibling apps are not stuck with WorkHub SMTP/SIEM/OTEL/OAuth names.
|
|
6
|
+
- SIEM `event_type` and CEF vendor follow `SIEM_EVENT_TYPE` / `APP_NAME` (defaults stay `workhub.audit` / `WorkHub`).
|
|
7
|
+
|
|
8
|
+
## 0.5.64
|
|
9
|
+
|
|
10
|
+
- `APP_KEY_PREFIX` (default `workhub`) namespaces Redis cache, queue, and throttle keys so sibling apps do not share WorkHub’s keyspace.
|
|
11
|
+
- `DispatchWebhookJob` implementation lives in the WorkHub webhook module. `@getstrata/core/jobs/dispatchWebhookJob` remains a compatibility re-export.
|
|
12
|
+
|
|
3
13
|
## 0.5.63
|
|
4
14
|
|
|
5
15
|
- Flash cookies honor `FLASH_COOKIE_NAME` (default `workhub_flash`) so sibling apps do not inherit WorkHub’s cookie name.
|
package/README.md
CHANGED
|
@@ -82,7 +82,7 @@ import type { Migration } from "@getstrata/core/database/migrations/types";
|
|
|
82
82
|
Package name: **`@getstrata/core`** (npm org [`@getstrata`](https://www.npmjs.com/org/getstrata)).
|
|
83
83
|
|
|
84
84
|
1. Add `NPM_TOKEN` to GitHub repository secrets.
|
|
85
|
-
2. Tag a release: `git tag v0.5.
|
|
85
|
+
2. Tag a release: `git tag v0.5.67 && git push origin v0.5.67`
|
|
86
86
|
3. [Release workflow](../../.github/workflows/release.yml) builds and runs `npm publish --access public`.
|
|
87
87
|
|
|
88
88
|
Previously published as `@eyk-workhub/framework@0.1.0`, deprecated in favor of this package.
|
|
@@ -1,16 +1 @@
|
|
|
1
|
-
|
|
2
|
-
interface DispatchWebhookPayload {
|
|
3
|
-
webhookId: number;
|
|
4
|
-
tenantId: number;
|
|
5
|
-
event: string;
|
|
6
|
-
payload: Record<string, unknown>;
|
|
7
|
-
}
|
|
8
|
-
declare class DispatchWebhookJob extends Job<DispatchWebhookPayload> {
|
|
9
|
-
readonly maxAttempts = 3;
|
|
10
|
-
readonly backoffMs = 2000;
|
|
11
|
-
handle(payload: DispatchWebhookPayload): Promise<void>;
|
|
12
|
-
private deliver;
|
|
13
|
-
}
|
|
14
|
-
export { DispatchWebhookJob };
|
|
15
|
-
export default DispatchWebhookJob;
|
|
16
|
-
export type { DispatchWebhookPayload };
|
|
1
|
+
export { DispatchWebhookJob, type DispatchWebhookPayload, default, } from "../../modules/webhook/dispatchWebhookJob";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type FailedJobService from "./failedJobService";
|
|
2
2
|
import type { Job, Queue, QueuePriority } from "./index";
|
|
3
3
|
import { type QueueJobEnvelope } from "./jobRunner";
|
|
4
|
-
declare const QUEUE_LIST_KEY
|
|
5
|
-
declare const QUEUE_HIGH_KEY
|
|
6
|
-
declare const QUEUE_LOW_KEY
|
|
4
|
+
declare const QUEUE_LIST_KEY: string;
|
|
5
|
+
declare const QUEUE_HIGH_KEY: string;
|
|
6
|
+
declare const QUEUE_LOW_KEY: string;
|
|
7
7
|
declare function queueKeyForPriority(priority?: QueuePriority): string;
|
|
8
8
|
declare function parseQueueJobEnvelope(rawPayload: string): QueueJobEnvelope | null;
|
|
9
9
|
declare class RedisQueue implements Queue {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
declare function appKeyPrefix(): string;
|
|
2
|
+
declare function namespacedRedisKey(kind: string): string;
|
|
3
|
+
declare function smtpEhloHost(): string;
|
|
4
|
+
declare function siemEventType(): string;
|
|
5
|
+
declare function appUserAgent(): string;
|
|
6
|
+
declare function otelServiceName(): string;
|
|
7
|
+
declare function webhookSignatureHeader(): string;
|
|
8
|
+
declare function appDisplayName(): string;
|
|
9
|
+
export { appDisplayName, appKeyPrefix, appUserAgent, namespacedRedisKey, otelServiceName, siemEventType, smtpEhloHost, webhookSignatureHeader, };
|
|
@@ -153,11 +153,39 @@ async function runWithMigrationBypass(callback) {
|
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
// ../../src/core/runtime/appKeyPrefix.ts
|
|
157
|
+
function appKeyPrefix() {
|
|
158
|
+
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
159
|
+
}
|
|
160
|
+
function namespacedRedisKey(kind) {
|
|
161
|
+
return `${appKeyPrefix()}:${kind}`;
|
|
162
|
+
}
|
|
163
|
+
function smtpEhloHost() {
|
|
164
|
+
const raw = process.env.MAIL_EHLO?.trim() || `${appKeyPrefix()}.local`;
|
|
165
|
+
const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
|
|
166
|
+
return safe || "strata.local";
|
|
167
|
+
}
|
|
168
|
+
function siemEventType() {
|
|
169
|
+
return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
|
|
170
|
+
}
|
|
171
|
+
function appUserAgent() {
|
|
172
|
+
return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
|
|
173
|
+
}
|
|
174
|
+
function otelServiceName() {
|
|
175
|
+
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
176
|
+
}
|
|
177
|
+
function webhookSignatureHeader() {
|
|
178
|
+
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
179
|
+
}
|
|
180
|
+
function appDisplayName() {
|
|
181
|
+
return process.env.APP_NAME?.trim() || "WorkHub";
|
|
182
|
+
}
|
|
183
|
+
|
|
156
184
|
// ../../src/core/audit/siemFormatter.ts
|
|
157
185
|
function formatSiemAuditEvent(input) {
|
|
158
186
|
return {
|
|
159
187
|
timestamp: input.created_at.toISOString(),
|
|
160
|
-
event_type:
|
|
188
|
+
event_type: siemEventType(),
|
|
161
189
|
actor_user_id: input.user_id,
|
|
162
190
|
tenant_id: input.tenant_id ?? null,
|
|
163
191
|
trace_id: input.trace_id ?? null,
|
|
@@ -183,7 +211,7 @@ function formatCefLine(event) {
|
|
|
183
211
|
`src=${event.ip_address ?? ""}`,
|
|
184
212
|
`request=${event.trace_id ?? ""}`
|
|
185
213
|
].join(" ");
|
|
186
|
-
return `CEF:0|
|
|
214
|
+
return `CEF:0|${appDisplayName()}|API|1.0|${event.action}|${event.subject_type}|5|${extension}`;
|
|
187
215
|
}
|
|
188
216
|
|
|
189
217
|
// ../../src/core/audit/exportAuditLogs.ts
|
|
@@ -1,9 +1,37 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
// ../../src/core/runtime/appKeyPrefix.ts
|
|
3
|
+
function appKeyPrefix() {
|
|
4
|
+
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
5
|
+
}
|
|
6
|
+
function namespacedRedisKey(kind) {
|
|
7
|
+
return `${appKeyPrefix()}:${kind}`;
|
|
8
|
+
}
|
|
9
|
+
function smtpEhloHost() {
|
|
10
|
+
const raw = process.env.MAIL_EHLO?.trim() || `${appKeyPrefix()}.local`;
|
|
11
|
+
const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
|
|
12
|
+
return safe || "strata.local";
|
|
13
|
+
}
|
|
14
|
+
function siemEventType() {
|
|
15
|
+
return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
|
|
16
|
+
}
|
|
17
|
+
function appUserAgent() {
|
|
18
|
+
return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
|
|
19
|
+
}
|
|
20
|
+
function otelServiceName() {
|
|
21
|
+
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
22
|
+
}
|
|
23
|
+
function webhookSignatureHeader() {
|
|
24
|
+
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
25
|
+
}
|
|
26
|
+
function appDisplayName() {
|
|
27
|
+
return process.env.APP_NAME?.trim() || "WorkHub";
|
|
28
|
+
}
|
|
29
|
+
|
|
2
30
|
// ../../src/core/audit/siemFormatter.ts
|
|
3
31
|
function formatSiemAuditEvent(input) {
|
|
4
32
|
return {
|
|
5
33
|
timestamp: input.created_at.toISOString(),
|
|
6
|
-
event_type:
|
|
34
|
+
event_type: siemEventType(),
|
|
7
35
|
actor_user_id: input.user_id,
|
|
8
36
|
tenant_id: input.tenant_id ?? null,
|
|
9
37
|
trace_id: input.trace_id ?? null,
|
|
@@ -29,7 +57,7 @@ function formatCefLine(event) {
|
|
|
29
57
|
`src=${event.ip_address ?? ""}`,
|
|
30
58
|
`request=${event.trace_id ?? ""}`
|
|
31
59
|
].join(" ");
|
|
32
|
-
return `CEF:0|
|
|
60
|
+
return `CEF:0|${appDisplayName()}|API|1.0|${event.action}|${event.subject_type}|5|${extension}`;
|
|
33
61
|
}
|
|
34
62
|
export {
|
|
35
63
|
formatCefLine,
|
|
@@ -1,4 +1,32 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
// ../../src/core/runtime/appKeyPrefix.ts
|
|
3
|
+
function appKeyPrefix() {
|
|
4
|
+
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
5
|
+
}
|
|
6
|
+
function namespacedRedisKey(kind) {
|
|
7
|
+
return `${appKeyPrefix()}:${kind}`;
|
|
8
|
+
}
|
|
9
|
+
function smtpEhloHost() {
|
|
10
|
+
const raw = process.env.MAIL_EHLO?.trim() || `${appKeyPrefix()}.local`;
|
|
11
|
+
const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
|
|
12
|
+
return safe || "strata.local";
|
|
13
|
+
}
|
|
14
|
+
function siemEventType() {
|
|
15
|
+
return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
|
|
16
|
+
}
|
|
17
|
+
function appUserAgent() {
|
|
18
|
+
return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
|
|
19
|
+
}
|
|
20
|
+
function otelServiceName() {
|
|
21
|
+
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
22
|
+
}
|
|
23
|
+
function webhookSignatureHeader() {
|
|
24
|
+
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
25
|
+
}
|
|
26
|
+
function appDisplayName() {
|
|
27
|
+
return process.env.APP_NAME?.trim() || "WorkHub";
|
|
28
|
+
}
|
|
29
|
+
|
|
2
30
|
// ../../src/core/auth/oauth/providers.ts
|
|
3
31
|
class GitHubOAuthProvider {
|
|
4
32
|
options;
|
|
@@ -37,7 +65,7 @@ class GitHubOAuthProvider {
|
|
|
37
65
|
headers: {
|
|
38
66
|
authorization: `Bearer ${tokenBody.access_token}`,
|
|
39
67
|
accept: "application/json",
|
|
40
|
-
"user-agent":
|
|
68
|
+
"user-agent": appUserAgent()
|
|
41
69
|
}
|
|
42
70
|
});
|
|
43
71
|
const profile = await profileResponse.json();
|
|
@@ -1,4 +1,32 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
// ../../src/core/runtime/appKeyPrefix.ts
|
|
3
|
+
function appKeyPrefix() {
|
|
4
|
+
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
5
|
+
}
|
|
6
|
+
function namespacedRedisKey(kind) {
|
|
7
|
+
return `${appKeyPrefix()}:${kind}`;
|
|
8
|
+
}
|
|
9
|
+
function smtpEhloHost() {
|
|
10
|
+
const raw = process.env.MAIL_EHLO?.trim() || `${appKeyPrefix()}.local`;
|
|
11
|
+
const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
|
|
12
|
+
return safe || "strata.local";
|
|
13
|
+
}
|
|
14
|
+
function siemEventType() {
|
|
15
|
+
return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
|
|
16
|
+
}
|
|
17
|
+
function appUserAgent() {
|
|
18
|
+
return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
|
|
19
|
+
}
|
|
20
|
+
function otelServiceName() {
|
|
21
|
+
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
22
|
+
}
|
|
23
|
+
function webhookSignatureHeader() {
|
|
24
|
+
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
25
|
+
}
|
|
26
|
+
function appDisplayName() {
|
|
27
|
+
return process.env.APP_NAME?.trim() || "WorkHub";
|
|
28
|
+
}
|
|
29
|
+
|
|
2
30
|
// ../../src/core/auth/oauth/samlProvider.ts
|
|
3
31
|
class SamlProvider {
|
|
4
32
|
loginUrl;
|
|
@@ -15,8 +43,8 @@ class SamlProvider {
|
|
|
15
43
|
}
|
|
16
44
|
const [, email, name] = code.split(":");
|
|
17
45
|
return {
|
|
18
|
-
providerUserId: email
|
|
19
|
-
email: email
|
|
46
|
+
providerUserId: email || "saml-user",
|
|
47
|
+
email: email || `saml-user@${appKeyPrefix()}.test`,
|
|
20
48
|
name: name ?? "SAML User"
|
|
21
49
|
};
|
|
22
50
|
}
|
|
@@ -1,8 +1,42 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// ../../src/core/cache/redisCacheStore.ts
|
|
3
3
|
var {RedisClient } = globalThis.Bun;
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
|
|
5
|
+
// ../../src/core/runtime/appKeyPrefix.ts
|
|
6
|
+
function appKeyPrefix() {
|
|
7
|
+
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
8
|
+
}
|
|
9
|
+
function namespacedRedisKey(kind) {
|
|
10
|
+
return `${appKeyPrefix()}:${kind}`;
|
|
11
|
+
}
|
|
12
|
+
function smtpEhloHost() {
|
|
13
|
+
const raw = process.env.MAIL_EHLO?.trim() || `${appKeyPrefix()}.local`;
|
|
14
|
+
const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
|
|
15
|
+
return safe || "strata.local";
|
|
16
|
+
}
|
|
17
|
+
function siemEventType() {
|
|
18
|
+
return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
|
|
19
|
+
}
|
|
20
|
+
function appUserAgent() {
|
|
21
|
+
return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
|
|
22
|
+
}
|
|
23
|
+
function otelServiceName() {
|
|
24
|
+
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
25
|
+
}
|
|
26
|
+
function webhookSignatureHeader() {
|
|
27
|
+
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
28
|
+
}
|
|
29
|
+
function appDisplayName() {
|
|
30
|
+
return process.env.APP_NAME?.trim() || "WorkHub";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ../../src/core/cache/redisCacheStore.ts
|
|
34
|
+
function cacheKeyPrefix() {
|
|
35
|
+
return namespacedRedisKey("cache:");
|
|
36
|
+
}
|
|
37
|
+
function cacheTagPrefix() {
|
|
38
|
+
return namespacedRedisKey("cache:tag:");
|
|
39
|
+
}
|
|
6
40
|
|
|
7
41
|
class RedisCacheStore {
|
|
8
42
|
ttlMs;
|
|
@@ -89,10 +123,10 @@ class RedisCacheStore {
|
|
|
89
123
|
return deleted > 0;
|
|
90
124
|
}
|
|
91
125
|
async invalidateByPrefix(prefix) {
|
|
92
|
-
const keys = await this.client.keys(`${
|
|
126
|
+
const keys = await this.client.keys(`${cacheKeyPrefix()}*`);
|
|
93
127
|
let removed = 0;
|
|
94
128
|
for (const storageKey of keys) {
|
|
95
|
-
const key = storageKey.slice(
|
|
129
|
+
const key = storageKey.slice(cacheKeyPrefix().length);
|
|
96
130
|
if (key === prefix || key.startsWith(`${prefix}?`)) {
|
|
97
131
|
if (await this.invalidate(key)) {
|
|
98
132
|
removed += 1;
|
|
@@ -102,11 +136,11 @@ class RedisCacheStore {
|
|
|
102
136
|
return removed;
|
|
103
137
|
}
|
|
104
138
|
async clear() {
|
|
105
|
-
const keys = await this.client.keys(`${
|
|
139
|
+
const keys = await this.client.keys(`${cacheKeyPrefix()}*`);
|
|
106
140
|
if (keys.length > 0) {
|
|
107
141
|
await this.client.del(...keys);
|
|
108
142
|
}
|
|
109
|
-
const tagKeys = await this.client.keys(`${
|
|
143
|
+
const tagKeys = await this.client.keys(`${cacheTagPrefix()}*`);
|
|
110
144
|
if (tagKeys.length > 0) {
|
|
111
145
|
await this.client.del(...tagKeys);
|
|
112
146
|
}
|
|
@@ -114,14 +148,14 @@ class RedisCacheStore {
|
|
|
114
148
|
this.keyTags.clear();
|
|
115
149
|
}
|
|
116
150
|
async size() {
|
|
117
|
-
const keys = await this.client.keys(`${
|
|
151
|
+
const keys = await this.client.keys(`${cacheKeyPrefix()}*`);
|
|
118
152
|
return keys.length;
|
|
119
153
|
}
|
|
120
154
|
storageKey(key) {
|
|
121
|
-
return `${
|
|
155
|
+
return `${cacheKeyPrefix()}${key}`;
|
|
122
156
|
}
|
|
123
157
|
tagKey(tag) {
|
|
124
|
-
return `${
|
|
158
|
+
return `${cacheTagPrefix()}${tag}`;
|
|
125
159
|
}
|
|
126
160
|
async detachKeyFromTags(key) {
|
|
127
161
|
const tags = this.keyTags.get(key);
|
|
@@ -134,7 +168,7 @@ class RedisCacheStore {
|
|
|
134
168
|
this.keyTags.delete(key);
|
|
135
169
|
}
|
|
136
170
|
async enforceMaxEntries() {
|
|
137
|
-
const keys = await this.client.keys(`${
|
|
171
|
+
const keys = await this.client.keys(`${cacheKeyPrefix()}*`);
|
|
138
172
|
if (keys.length <= this.maxEntries) {
|
|
139
173
|
return;
|
|
140
174
|
}
|
package/dist/entries/facades.js
CHANGED
|
@@ -11,6 +11,34 @@ import {
|
|
|
11
11
|
resolveApplicationQueue
|
|
12
12
|
} from "@getstrata/core/runtime/applicationRegistry";
|
|
13
13
|
|
|
14
|
+
// ../../src/core/runtime/appKeyPrefix.ts
|
|
15
|
+
function appKeyPrefix() {
|
|
16
|
+
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
17
|
+
}
|
|
18
|
+
function namespacedRedisKey(kind) {
|
|
19
|
+
return `${appKeyPrefix()}:${kind}`;
|
|
20
|
+
}
|
|
21
|
+
function smtpEhloHost() {
|
|
22
|
+
const raw = process.env.MAIL_EHLO?.trim() || `${appKeyPrefix()}.local`;
|
|
23
|
+
const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
|
|
24
|
+
return safe || "strata.local";
|
|
25
|
+
}
|
|
26
|
+
function siemEventType() {
|
|
27
|
+
return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
|
|
28
|
+
}
|
|
29
|
+
function appUserAgent() {
|
|
30
|
+
return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
|
|
31
|
+
}
|
|
32
|
+
function otelServiceName() {
|
|
33
|
+
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
34
|
+
}
|
|
35
|
+
function webhookSignatureHeader() {
|
|
36
|
+
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
37
|
+
}
|
|
38
|
+
function appDisplayName() {
|
|
39
|
+
return process.env.APP_NAME?.trim() || "WorkHub";
|
|
40
|
+
}
|
|
41
|
+
|
|
14
42
|
// ../../src/core/mail/mailer.ts
|
|
15
43
|
function resolveSmtpConfig() {
|
|
16
44
|
const host = process.env.MAIL_HOST?.trim();
|
|
@@ -101,7 +129,7 @@ async function defaultSmtpTransport(config, message) {
|
|
|
101
129
|
const { socket, readResponse } = await openSmtpConnection(config);
|
|
102
130
|
try {
|
|
103
131
|
await waitForSmtpResponse(readResponse, ["220"]);
|
|
104
|
-
await socket.write(`EHLO
|
|
132
|
+
await socket.write(`EHLO ${smtpEhloHost()}\r
|
|
105
133
|
`);
|
|
106
134
|
await waitForSmtpResponse(readResponse, ["250"]);
|
|
107
135
|
if (config.username && config.password) {
|
|
@@ -2,6 +2,34 @@
|
|
|
2
2
|
// ../../src/core/http/loginThrottleMiddleware.ts
|
|
3
3
|
var {RedisClient } = globalThis.Bun;
|
|
4
4
|
|
|
5
|
+
// ../../src/core/runtime/appKeyPrefix.ts
|
|
6
|
+
function appKeyPrefix() {
|
|
7
|
+
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
8
|
+
}
|
|
9
|
+
function namespacedRedisKey(kind) {
|
|
10
|
+
return `${appKeyPrefix()}:${kind}`;
|
|
11
|
+
}
|
|
12
|
+
function smtpEhloHost() {
|
|
13
|
+
const raw = process.env.MAIL_EHLO?.trim() || `${appKeyPrefix()}.local`;
|
|
14
|
+
const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
|
|
15
|
+
return safe || "strata.local";
|
|
16
|
+
}
|
|
17
|
+
function siemEventType() {
|
|
18
|
+
return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
|
|
19
|
+
}
|
|
20
|
+
function appUserAgent() {
|
|
21
|
+
return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
|
|
22
|
+
}
|
|
23
|
+
function otelServiceName() {
|
|
24
|
+
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
25
|
+
}
|
|
26
|
+
function webhookSignatureHeader() {
|
|
27
|
+
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
28
|
+
}
|
|
29
|
+
function appDisplayName() {
|
|
30
|
+
return process.env.APP_NAME?.trim() || "WorkHub";
|
|
31
|
+
}
|
|
32
|
+
|
|
5
33
|
// ../../src/core/http/clientIp.ts
|
|
6
34
|
function trustForwardedFor(env = process.env) {
|
|
7
35
|
return (env.TRUST_FORWARDED_FOR ?? "false") === "true";
|
|
@@ -37,7 +65,7 @@ async function resolveLoginEmail(request) {
|
|
|
37
65
|
}
|
|
38
66
|
function createLoginThrottleMiddleware(options) {
|
|
39
67
|
const client = new RedisClient(options.redisUrl);
|
|
40
|
-
const prefix = options.keyPrefix ?? "
|
|
68
|
+
const prefix = options.keyPrefix ?? namespacedRedisKey("login-throttle:");
|
|
41
69
|
return async (request, next) => {
|
|
42
70
|
const identity = resolveLoginIdentity(request);
|
|
43
71
|
const email = await resolveLoginEmail(request);
|
|
@@ -1,4 +1,32 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
// ../../src/core/runtime/appKeyPrefix.ts
|
|
3
|
+
function appKeyPrefix() {
|
|
4
|
+
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
5
|
+
}
|
|
6
|
+
function namespacedRedisKey(kind) {
|
|
7
|
+
return `${appKeyPrefix()}:${kind}`;
|
|
8
|
+
}
|
|
9
|
+
function smtpEhloHost() {
|
|
10
|
+
const raw = process.env.MAIL_EHLO?.trim() || `${appKeyPrefix()}.local`;
|
|
11
|
+
const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
|
|
12
|
+
return safe || "strata.local";
|
|
13
|
+
}
|
|
14
|
+
function siemEventType() {
|
|
15
|
+
return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
|
|
16
|
+
}
|
|
17
|
+
function appUserAgent() {
|
|
18
|
+
return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
|
|
19
|
+
}
|
|
20
|
+
function otelServiceName() {
|
|
21
|
+
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
22
|
+
}
|
|
23
|
+
function webhookSignatureHeader() {
|
|
24
|
+
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
25
|
+
}
|
|
26
|
+
function appDisplayName() {
|
|
27
|
+
return process.env.APP_NAME?.trim() || "WorkHub";
|
|
28
|
+
}
|
|
29
|
+
|
|
2
30
|
// ../../src/core/http/clientIp.ts
|
|
3
31
|
function trustForwardedFor(env = process.env) {
|
|
4
32
|
return (env.TRUST_FORWARDED_FOR ?? "false") === "true";
|
|
@@ -17,7 +45,7 @@ function readClientIp(request, env = process.env) {
|
|
|
17
45
|
// ../../src/core/http/memoryThrottleMiddleware.ts
|
|
18
46
|
var throttleBucketRegistries = new Set;
|
|
19
47
|
function createMemoryThrottleMiddleware(options) {
|
|
20
|
-
const prefix = options.keyPrefix ?? "
|
|
48
|
+
const prefix = options.keyPrefix ?? namespacedRedisKey("memory-throttle:");
|
|
21
49
|
const buckets = new Map;
|
|
22
50
|
throttleBucketRegistries.add(buckets);
|
|
23
51
|
return async (request, next) => {
|
|
@@ -2,6 +2,34 @@
|
|
|
2
2
|
// ../../src/core/http/scimThrottleMiddleware.ts
|
|
3
3
|
var {RedisClient } = globalThis.Bun;
|
|
4
4
|
|
|
5
|
+
// ../../src/core/runtime/appKeyPrefix.ts
|
|
6
|
+
function appKeyPrefix() {
|
|
7
|
+
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
8
|
+
}
|
|
9
|
+
function namespacedRedisKey(kind) {
|
|
10
|
+
return `${appKeyPrefix()}:${kind}`;
|
|
11
|
+
}
|
|
12
|
+
function smtpEhloHost() {
|
|
13
|
+
const raw = process.env.MAIL_EHLO?.trim() || `${appKeyPrefix()}.local`;
|
|
14
|
+
const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
|
|
15
|
+
return safe || "strata.local";
|
|
16
|
+
}
|
|
17
|
+
function siemEventType() {
|
|
18
|
+
return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
|
|
19
|
+
}
|
|
20
|
+
function appUserAgent() {
|
|
21
|
+
return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
|
|
22
|
+
}
|
|
23
|
+
function otelServiceName() {
|
|
24
|
+
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
25
|
+
}
|
|
26
|
+
function webhookSignatureHeader() {
|
|
27
|
+
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
28
|
+
}
|
|
29
|
+
function appDisplayName() {
|
|
30
|
+
return process.env.APP_NAME?.trim() || "WorkHub";
|
|
31
|
+
}
|
|
32
|
+
|
|
5
33
|
// ../../src/core/http/clientIp.ts
|
|
6
34
|
function trustForwardedFor(env = process.env) {
|
|
7
35
|
return (env.TRUST_FORWARDED_FOR ?? "false") === "true";
|
|
@@ -39,7 +67,7 @@ function createScimThrottleMiddleware(options) {
|
|
|
39
67
|
const client = options.redisUrl ? new RedisClient(options.redisUrl) : null;
|
|
40
68
|
return async (request, next) => {
|
|
41
69
|
const identity = resolveScimIdentity(request);
|
|
42
|
-
const key =
|
|
70
|
+
const key = `${namespacedRedisKey("scim-throttle:")}${identity}`;
|
|
43
71
|
if (client) {
|
|
44
72
|
const attempts = Number(await client.incr(key));
|
|
45
73
|
if (attempts === 1) {
|
|
@@ -4,6 +4,34 @@ import { currentAuthUser } from "@getstrata/core/auth/authContext";
|
|
|
4
4
|
import { currentTenant, rateLimitMultiplierForPlan } from "@getstrata/core/tenant/tenantContext";
|
|
5
5
|
var {RedisClient } = globalThis.Bun;
|
|
6
6
|
|
|
7
|
+
// ../../src/core/runtime/appKeyPrefix.ts
|
|
8
|
+
function appKeyPrefix() {
|
|
9
|
+
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
10
|
+
}
|
|
11
|
+
function namespacedRedisKey(kind) {
|
|
12
|
+
return `${appKeyPrefix()}:${kind}`;
|
|
13
|
+
}
|
|
14
|
+
function smtpEhloHost() {
|
|
15
|
+
const raw = process.env.MAIL_EHLO?.trim() || `${appKeyPrefix()}.local`;
|
|
16
|
+
const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
|
|
17
|
+
return safe || "strata.local";
|
|
18
|
+
}
|
|
19
|
+
function siemEventType() {
|
|
20
|
+
return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
|
|
21
|
+
}
|
|
22
|
+
function appUserAgent() {
|
|
23
|
+
return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
|
|
24
|
+
}
|
|
25
|
+
function otelServiceName() {
|
|
26
|
+
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
27
|
+
}
|
|
28
|
+
function webhookSignatureHeader() {
|
|
29
|
+
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
30
|
+
}
|
|
31
|
+
function appDisplayName() {
|
|
32
|
+
return process.env.APP_NAME?.trim() || "WorkHub";
|
|
33
|
+
}
|
|
34
|
+
|
|
7
35
|
// ../../src/core/http/clientIp.ts
|
|
8
36
|
function trustForwardedFor(env = process.env) {
|
|
9
37
|
return (env.TRUST_FORWARDED_FOR ?? "false") === "true";
|
|
@@ -32,7 +60,7 @@ function resolveThrottleIdentity(request) {
|
|
|
32
60
|
}
|
|
33
61
|
function createThrottleMiddleware(options) {
|
|
34
62
|
const client = new RedisClient(options.redisUrl);
|
|
35
|
-
const prefix = options.keyPrefix ?? "
|
|
63
|
+
const prefix = options.keyPrefix ?? namespacedRedisKey("throttle:");
|
|
36
64
|
return async (request, next) => {
|
|
37
65
|
const identity = resolveThrottleIdentity(request);
|
|
38
66
|
const path = new URL(request.url).pathname;
|