@sentroy-co/client-sdk 2.8.0 → 2.12.0
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/AGENTS.md +97 -0
- package/README.md +34 -1
- package/bin/sentroy.js +4 -0
- package/dist/cli/dotenv.d.ts +35 -0
- package/dist/cli/dotenv.d.ts.map +1 -0
- package/dist/cli/dotenv.js +125 -0
- package/dist/cli/dotenv.js.map +1 -0
- package/dist/cli/env.d.ts +11 -0
- package/dist/cli/env.d.ts.map +1 -0
- package/dist/cli/env.js +331 -0
- package/dist/cli/env.js.map +1 -0
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +105 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/vault/index.d.ts +60 -0
- package/dist/vault/index.d.ts.map +1 -1
- package/dist/vault/index.js +116 -0
- package/dist/vault/index.js.map +1 -1
- package/package.json +9 -3
- package/src/cli/dotenv.ts +146 -0
- package/src/cli/env.ts +402 -0
- package/src/cli/index.ts +122 -0
- package/src/vault/index.ts +160 -0
package/dist/vault/index.js
CHANGED
|
@@ -32,8 +32,10 @@ exports.refreshEnvCache = refreshEnvCache;
|
|
|
32
32
|
exports.preloadEnv = preloadEnv;
|
|
33
33
|
exports.getEnv = getEnv;
|
|
34
34
|
exports.getEnvOrThrow = getEnvOrThrow;
|
|
35
|
+
exports.getEnvWithFallback = getEnvWithFallback;
|
|
35
36
|
exports.getAllEnvs = getAllEnvs;
|
|
36
37
|
exports.getPublicEnvs = getPublicEnvs;
|
|
38
|
+
exports.createVaultWebhookHandler = createVaultWebhookHandler;
|
|
37
39
|
const DEFAULT_TTL_MS = 5 * 60 * 1000;
|
|
38
40
|
const DEFAULT_BASE_URL = "https://sentroy.com";
|
|
39
41
|
let resolvedBaseUrl = DEFAULT_BASE_URL;
|
|
@@ -148,6 +150,38 @@ async function getEnvOrThrow(key) {
|
|
|
148
150
|
}
|
|
149
151
|
return v;
|
|
150
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* Migration helper — vault'tan oku, yoksa `process.env` fallback.
|
|
155
|
+
*
|
|
156
|
+
* Sentroy app'lerini kademeli olarak `process.env` → vault'a çevirirken
|
|
157
|
+
* "her ikisi de çalışsın" senaryosu için. Vault doldurulmamış / token
|
|
158
|
+
* eksik / fetch fail dönerse sessizce `process.env[key]`'e döner — eski
|
|
159
|
+
* deploy ile yeni kod bir arada çalışabilir.
|
|
160
|
+
*
|
|
161
|
+
* **Migration tamamlandıktan sonra** çağrı sitelerini `getEnv()` ya da
|
|
162
|
+
* `getEnvOrThrow()`'a çevir; fallback'i bırakmak silently process.env
|
|
163
|
+
* sızıntısı riskini taşır (kullanıcı vault'tan key'i sildi sansa bile
|
|
164
|
+
* eski process.env değeri etkili olur).
|
|
165
|
+
*
|
|
166
|
+
* Bootstrap path için (`SENTROY_ENV_API_KEY` set değil) doğrudan
|
|
167
|
+
* `process.env`'e döner — vault fetch denemez. Bu önemli: Sentroy app'i
|
|
168
|
+
* vault'sız boot edilebilir.
|
|
169
|
+
*/
|
|
170
|
+
async function getEnvWithFallback(key) {
|
|
171
|
+
// Token yoksa bypass — vault fetch denemeyelim, log spam etmeyelim.
|
|
172
|
+
const apiKey = resolvedApiKey ?? readEnv("SENTROY_ENV_API_KEY");
|
|
173
|
+
if (!apiKey)
|
|
174
|
+
return readEnv(key);
|
|
175
|
+
try {
|
|
176
|
+
const v = await getEnv(key);
|
|
177
|
+
if (v !== undefined)
|
|
178
|
+
return v;
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
// Fetch fail / network down / 401 → sessizce fallback
|
|
182
|
+
}
|
|
183
|
+
return readEnv(key);
|
|
184
|
+
}
|
|
151
185
|
/** Tüm env'leri map olarak döner (dump için kullanışlı). */
|
|
152
186
|
async function getAllEnvs() {
|
|
153
187
|
const c = await ensureCache();
|
|
@@ -166,4 +200,86 @@ async function getPublicEnvs() {
|
|
|
166
200
|
}
|
|
167
201
|
return out;
|
|
168
202
|
}
|
|
203
|
+
const DEFAULT_MAX_AGE_MS = 5 * 60 * 1000;
|
|
204
|
+
async function timingSafeEqualHex(a, b) {
|
|
205
|
+
if (a.length !== b.length)
|
|
206
|
+
return false;
|
|
207
|
+
let diff = 0;
|
|
208
|
+
for (let i = 0; i < a.length; i++) {
|
|
209
|
+
diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
210
|
+
}
|
|
211
|
+
return diff === 0;
|
|
212
|
+
}
|
|
213
|
+
async function hmacSha256Hex(secret, body) {
|
|
214
|
+
// Web Crypto — Node 18+ + browser ikisi de destekler.
|
|
215
|
+
const encoder = new TextEncoder();
|
|
216
|
+
const key = await crypto.subtle.importKey("raw", encoder.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
217
|
+
const sig = await crypto.subtle.sign("HMAC", key, encoder.encode(body));
|
|
218
|
+
const bytes = new Uint8Array(sig);
|
|
219
|
+
let hex = "";
|
|
220
|
+
for (const b of bytes)
|
|
221
|
+
hex += b.toString(16).padStart(2, "0");
|
|
222
|
+
return hex;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Bir Sentroy vault webhook receiver'ı için Request → Response handler
|
|
226
|
+
* üretir. Next.js App Router'da:
|
|
227
|
+
*
|
|
228
|
+
* // app/api/sentroy/vault-webhook/route.ts
|
|
229
|
+
* import { createVaultWebhookHandler } from "@sentroy-co/client-sdk/vault"
|
|
230
|
+
* export const POST = createVaultWebhookHandler({
|
|
231
|
+
* secret: process.env.SENTROY_VAULT_WEBHOOK_SECRET!,
|
|
232
|
+
* })
|
|
233
|
+
*
|
|
234
|
+
* Default davranış: imza doğruysa cache'i invalidate eder ve 200 döner.
|
|
235
|
+
* Hatalı/eksik imza → 401, eski timestamp → 401, body parse hatası → 400.
|
|
236
|
+
*/
|
|
237
|
+
function createVaultWebhookHandler(options) {
|
|
238
|
+
const maxAgeMs = options.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
|
|
239
|
+
return async (request) => {
|
|
240
|
+
const sigHeader = request.headers.get("x-sentroy-signature") || "";
|
|
241
|
+
const match = sigHeader.match(/^sha256=([a-f0-9]+)$/i);
|
|
242
|
+
if (!match) {
|
|
243
|
+
return new Response("missing or malformed X-Sentroy-Signature header", {
|
|
244
|
+
status: 401,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
const providedSig = match[1].toLowerCase();
|
|
248
|
+
const body = await request.text();
|
|
249
|
+
const expected = await hmacSha256Hex(options.secret, body);
|
|
250
|
+
if (!(await timingSafeEqualHex(providedSig, expected))) {
|
|
251
|
+
return new Response("signature mismatch", { status: 401 });
|
|
252
|
+
}
|
|
253
|
+
let payload;
|
|
254
|
+
try {
|
|
255
|
+
payload = JSON.parse(body);
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
return new Response("invalid JSON body", { status: 400 });
|
|
259
|
+
}
|
|
260
|
+
if (maxAgeMs > 0) {
|
|
261
|
+
const age = Date.now() - (payload.timestamp ?? 0);
|
|
262
|
+
if (!Number.isFinite(age) || age < 0 || age > maxAgeMs) {
|
|
263
|
+
return new Response("payload timestamp outside acceptable window", {
|
|
264
|
+
status: 401,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
try {
|
|
269
|
+
if (options.onChange) {
|
|
270
|
+
await options.onChange(payload);
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
await refreshEnvCache();
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
catch (err) {
|
|
277
|
+
return new Response(`handler error: ${err instanceof Error ? err.message : String(err)}`, { status: 500 });
|
|
278
|
+
}
|
|
279
|
+
return new Response(JSON.stringify({ ok: true }), {
|
|
280
|
+
status: 200,
|
|
281
|
+
headers: { "Content-Type": "application/json" },
|
|
282
|
+
});
|
|
283
|
+
};
|
|
284
|
+
}
|
|
169
285
|
//# sourceMappingURL=index.js.map
|
package/dist/vault/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vault/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;;AA8CH,gDAYC;AAGD,wCAEC;AAGD,0CAGC;AAGD,gCAEC;AAkED,wBAGC;AAGD,sCAQC;AAGD,gCAKC;AAGD,sCAOC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vault/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentroy-co/client-sdk",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "TypeScript SDK for the Sentroy platform — mail, storage, env vault + React components.",
|
|
3
|
+
"version": "2.12.0",
|
|
4
|
+
"description": "TypeScript SDK + CLI for the Sentroy platform — mail, storage, env vault + React components.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"sentroy": "./bin/sentroy.js"
|
|
9
|
+
},
|
|
7
10
|
"exports": {
|
|
8
11
|
".": {
|
|
9
12
|
"types": "./dist/index.d.ts",
|
|
@@ -39,6 +42,7 @@
|
|
|
39
42
|
"files": [
|
|
40
43
|
"dist",
|
|
41
44
|
"src",
|
|
45
|
+
"bin",
|
|
42
46
|
"AGENTS.md"
|
|
43
47
|
],
|
|
44
48
|
"scripts": {
|
|
@@ -57,7 +61,9 @@
|
|
|
57
61
|
"env",
|
|
58
62
|
"vault",
|
|
59
63
|
"secrets",
|
|
60
|
-
"config"
|
|
64
|
+
"config",
|
|
65
|
+
"cli",
|
|
66
|
+
"dotenv"
|
|
61
67
|
],
|
|
62
68
|
"repository": {
|
|
63
69
|
"type": "git",
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal .env parser + serializer used by the CLI.
|
|
3
|
+
*
|
|
4
|
+
* Format conventions (mirrors apps/core/components/admin/env-vault-content.tsx
|
|
5
|
+
* developer mode):
|
|
6
|
+
* - blank line resets pending description/public flag
|
|
7
|
+
* - `# @public` on its own line marks the next variable as browser-readable
|
|
8
|
+
* - `# any other text` becomes the next variable's description
|
|
9
|
+
* - `KEY=value` (unquoted)
|
|
10
|
+
* - `KEY="value with spaces"` (double-quoted; supports \n, \", \\ escapes)
|
|
11
|
+
* - `KEY='single quotes'` (single-quoted; literal)
|
|
12
|
+
* - `export KEY=value` (export prefix stripped)
|
|
13
|
+
*
|
|
14
|
+
* Anything else is reported as a parse error with the offending line number.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export interface DotenvEntry {
|
|
18
|
+
key: string
|
|
19
|
+
value: string
|
|
20
|
+
public: boolean
|
|
21
|
+
description: string | null
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface DotenvParseResult {
|
|
25
|
+
entries: DotenvEntry[]
|
|
26
|
+
errors: { line: number; message: string }[]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const KEY_PATTERN = /^[A-Z_][A-Z0-9_]*$/
|
|
30
|
+
|
|
31
|
+
export function parseDotenv(text: string): DotenvParseResult {
|
|
32
|
+
const lines = text.split(/\r?\n/)
|
|
33
|
+
const entries: DotenvEntry[] = []
|
|
34
|
+
const errors: DotenvParseResult["errors"] = []
|
|
35
|
+
const seen = new Set<string>()
|
|
36
|
+
|
|
37
|
+
let pendingDescription: string[] = []
|
|
38
|
+
let pendingPublic = false
|
|
39
|
+
|
|
40
|
+
for (let i = 0; i < lines.length; i++) {
|
|
41
|
+
const raw = lines[i] ?? ""
|
|
42
|
+
const line = raw.trim()
|
|
43
|
+
|
|
44
|
+
if (line === "") {
|
|
45
|
+
pendingDescription = []
|
|
46
|
+
pendingPublic = false
|
|
47
|
+
continue
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (line.startsWith("#")) {
|
|
51
|
+
const body = line.slice(1).trim()
|
|
52
|
+
if (body === "@public") {
|
|
53
|
+
pendingPublic = true
|
|
54
|
+
} else if (body) {
|
|
55
|
+
pendingDescription.push(body)
|
|
56
|
+
}
|
|
57
|
+
continue
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const work = line.startsWith("export ") ? line.slice(7).trimStart() : line
|
|
61
|
+
const eq = work.indexOf("=")
|
|
62
|
+
if (eq <= 0) {
|
|
63
|
+
errors.push({
|
|
64
|
+
line: i + 1,
|
|
65
|
+
message: "invalid syntax (expected KEY=value)",
|
|
66
|
+
})
|
|
67
|
+
pendingDescription = []
|
|
68
|
+
pendingPublic = false
|
|
69
|
+
continue
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const key = work.slice(0, eq).trim()
|
|
73
|
+
let value = work.slice(eq + 1)
|
|
74
|
+
|
|
75
|
+
if (!KEY_PATTERN.test(key)) {
|
|
76
|
+
errors.push({
|
|
77
|
+
line: i + 1,
|
|
78
|
+
message: "key must match [A-Z_][A-Z0-9_]*",
|
|
79
|
+
})
|
|
80
|
+
pendingDescription = []
|
|
81
|
+
pendingPublic = false
|
|
82
|
+
continue
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const trimmed = value.trim()
|
|
86
|
+
if (
|
|
87
|
+
(trimmed.startsWith('"') && trimmed.endsWith('"')) ||
|
|
88
|
+
(trimmed.startsWith("'") && trimmed.endsWith("'"))
|
|
89
|
+
) {
|
|
90
|
+
value = trimmed.slice(1, -1)
|
|
91
|
+
if (trimmed.startsWith('"')) {
|
|
92
|
+
value = value
|
|
93
|
+
.replace(/\\n/g, "\n")
|
|
94
|
+
.replace(/\\"/g, '"')
|
|
95
|
+
.replace(/\\\\/g, "\\")
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
value = trimmed
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (seen.has(key)) {
|
|
102
|
+
errors.push({ line: i + 1, message: `duplicate key ${key}` })
|
|
103
|
+
pendingDescription = []
|
|
104
|
+
pendingPublic = false
|
|
105
|
+
continue
|
|
106
|
+
}
|
|
107
|
+
seen.add(key)
|
|
108
|
+
|
|
109
|
+
entries.push({
|
|
110
|
+
key,
|
|
111
|
+
value,
|
|
112
|
+
public: pendingPublic,
|
|
113
|
+
description:
|
|
114
|
+
pendingDescription.length > 0 ? pendingDescription.join(" ") : null,
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
pendingDescription = []
|
|
118
|
+
pendingPublic = false
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return { entries, errors }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Inverse of parseDotenv — emit a .env document that round-trips through it.
|
|
126
|
+
* Quotes values that contain whitespace or shell-special characters.
|
|
127
|
+
*/
|
|
128
|
+
export function serializeDotenv(entries: DotenvEntry[]): string {
|
|
129
|
+
const blocks: string[] = []
|
|
130
|
+
for (const e of entries) {
|
|
131
|
+
const parts: string[] = []
|
|
132
|
+
if (e.description) parts.push(`# ${e.description}`)
|
|
133
|
+
if (e.public) parts.push("# @public")
|
|
134
|
+
const value = e.value
|
|
135
|
+
const needsQuote = /[\s"'#$`\\]/.test(value) || value === ""
|
|
136
|
+
const escaped = needsQuote
|
|
137
|
+
? `"${value
|
|
138
|
+
.replace(/\\/g, "\\\\")
|
|
139
|
+
.replace(/"/g, '\\"')
|
|
140
|
+
.replace(/\n/g, "\\n")}"`
|
|
141
|
+
: value
|
|
142
|
+
parts.push(`${e.key}=${escaped}`)
|
|
143
|
+
blocks.push(parts.join("\n"))
|
|
144
|
+
}
|
|
145
|
+
return blocks.join("\n\n") + (blocks.length > 0 ? "\n" : "")
|
|
146
|
+
}
|